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(ch: 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: QString{}, create: true);
560 fontStyle->styleName = stylename;
561 fontStyle->smoothScalable = scalable;
562 fontStyle->antialiased = antialiased;
563 QtFontSize *size = fontStyle->pixelSize(size: pixelSize ? pixelSize : SMOOTH_SCALABLE, add: true);
564 if (size->handle) {
565 QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration();
566 if (integration)
567 integration->fontDatabase()->releaseHandle(handle: size->handle);
568 }
569 size->handle = handle;
570 f->populated = true;
571}
572
573void qt_registerFontFamily(const QString &familyName)
574{
575 qCDebug(lcFontDb) << "Registering family" << familyName;
576
577 // Create uninitialized/unpopulated family
578 QFontDatabasePrivate::instance()->family(f: familyName, flags: QFontDatabasePrivate::EnsureCreated);
579}
580
581void qt_registerAliasToFontFamily(const QString &familyName, const QString &alias)
582{
583 if (alias.isEmpty())
584 return;
585
586 qCDebug(lcFontDb) << "Registering alias" << alias << "to family" << familyName;
587
588 auto *d = QFontDatabasePrivate::instance();
589 QtFontFamily *f = d->family(f: familyName, flags: QFontDatabasePrivate::RequestFamily);
590 if (!f)
591 return;
592
593 if (f->aliases.contains(str: alias, cs: Qt::CaseInsensitive))
594 return;
595
596 f->aliases.push_back(t: alias);
597}
598
599QString qt_resolveFontFamilyAlias(const QString &alias)
600{
601 if (!alias.isEmpty()) {
602 const auto *d = QFontDatabasePrivate::instance();
603 for (int i = 0; i < d->count; ++i)
604 if (d->families[i]->matchesFamilyName(familyName: alias))
605 return d->families[i]->name;
606 }
607 return alias;
608}
609
610bool qt_isFontFamilyPopulated(const QString &familyName)
611{
612 auto *d = QFontDatabasePrivate::instance();
613 QtFontFamily *f = d->family(f: familyName, flags: QFontDatabasePrivate::RequestFamily);
614 return f != nullptr && f->populated;
615}
616
617/*!
618 Returns a list of alternative fonts for the specified \a family and
619 \a style and \a script using the \a styleHint given.
620
621 Default implementation returns a list of fonts for which \a style and \a script support
622 has been reported during the font database population.
623*/
624QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
625{
626 Q_UNUSED(family);
627 Q_UNUSED(styleHint);
628
629 QStringList preferredFallbacks;
630 QStringList otherFallbacks;
631
632 auto writingSystem = qt_writing_system_for_script(script);
633 if (writingSystem >= QFontDatabase::WritingSystemsCount)
634 writingSystem = QFontDatabase::Any;
635
636 auto *db = QFontDatabasePrivate::instance();
637 for (int i = 0; i < db->count; ++i) {
638 QtFontFamily *f = db->families[i];
639
640 f->ensurePopulated();
641
642 if (writingSystem != QFontDatabase::Any && !familySupportsWritingSystem(family: f, writingSystem))
643 continue;
644
645 for (int j = 0; j < f->count; ++j) {
646 QtFontFoundry *foundry = f->foundries[j];
647
648 for (int k = 0; k < foundry->count; ++k) {
649 QString name = foundry->name.isEmpty()
650 ? f->name
651 : f->name + " ["_L1 + foundry->name + u']';
652 if (style == foundry->styles[k]->key.style)
653 preferredFallbacks.append(t: name);
654 else
655 otherFallbacks.append(t: name);
656 }
657 }
658 }
659
660 return preferredFallbacks + otherFallbacks;
661}
662
663static QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script)
664{
665 QMutexLocker locker(fontDatabaseMutex());
666 auto *db = QFontDatabasePrivate::ensureFontDatabase();
667
668 const QtFontFallbacksCacheKey cacheKey = { .family: family, .style: style, .styleHint: styleHint, .script: script };
669
670 if (const QStringList *fallbacks = db->fallbacksCache.object(key: cacheKey))
671 return *fallbacks;
672
673 // make sure that the db has all fallback families
674 QStringList userFallbacks = db->applicationFallbackFontFamilies.value(key: script == QChar::Script_Latin ? QChar::Script_Common : script);
675 QStringList retList = userFallbacks + QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script);
676
677 QStringList::iterator i;
678 for (i = retList.begin(); i != retList.end(); ++i) {
679 bool contains = false;
680 for (int j = 0; j < db->count; j++) {
681 if (db->families[j]->matchesFamilyName(familyName: *i)) {
682 contains = true;
683 break;
684 }
685 }
686 if (!contains) {
687 i = retList.erase(pos: i);
688 --i;
689 }
690 }
691
692 db->fallbacksCache.insert(key: cacheKey, object: new QStringList(retList));
693
694 return retList;
695}
696
697QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script)
698{
699 QMutexLocker locker(fontDatabaseMutex());
700 return fallbacksForFamily(family, style, styleHint, script);
701}
702
703QFontEngine *QFontDatabasePrivate::loadSingleEngine(int script,
704 const QFontDef &request,
705 QtFontFamily *family, QtFontFoundry *foundry,
706 QtFontStyle *style, QtFontSize *size)
707{
708 Q_UNUSED(foundry);
709
710 Q_ASSERT(size);
711 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
712 int pixelSize = size->pixelSize;
713 if (!pixelSize || (style->smoothScalable && pixelSize == SMOOTH_SCALABLE)
714 || pfdb->fontsAlwaysScalable()) {
715 pixelSize = request.pixelSize;
716 }
717
718 QFontDef def = request;
719 def.pixelSize = pixelSize;
720
721 QFontCache *fontCache = QFontCache::instance();
722
723 QFontCache::Key key(def,script);
724 QFontEngine *engine = fontCache->findEngine(key);
725 if (!engine) {
726 const bool cacheForCommonScript = script != QChar::Script_Common
727 && (family->writingSystems[QFontDatabase::Latin] & QtFontFamily::Supported) != 0;
728
729 if (Q_LIKELY(cacheForCommonScript)) {
730 // fast path: check if engine was loaded for another script
731 key.script = QChar::Script_Common;
732 engine = fontCache->findEngine(key);
733 key.script = script;
734 if (engine) {
735 // Also check for OpenType tables when using complex scripts
736 if (Q_UNLIKELY(!engine->supportsScript(QChar::Script(script)))) {
737 qCInfo(lcFontDb, "OpenType support missing for \"%ls\", script %d",
738 qUtf16Printable(def.families.constFirst()), script);
739 return nullptr;
740 }
741
742 engine->isSmoothlyScalable = style->smoothScalable;
743 fontCache->insertEngine(key, engine);
744 return engine;
745 }
746 }
747
748 // To avoid synthesized stretch we need a matching stretch to be 100 after this point.
749 // If stretch didn't match exactly we need to calculate the new stretch factor.
750 // This only done if not matched by styleName.
751 if (style->key.stretch != 0 && request.stretch != 0
752 && (request.styleName.isEmpty() || request.styleName != style->styleName)) {
753 def.stretch = (request.stretch * 100 + style->key.stretch / 2) / style->key.stretch;
754 } else if (request.stretch == QFont::AnyStretch) {
755 def.stretch = 100;
756 }
757
758 engine = pfdb->fontEngine(fontDef: def, handle: size->handle);
759 if (engine) {
760 // Also check for OpenType tables when using complex scripts
761 if (!engine->supportsScript(script: QChar::Script(script))) {
762 qCInfo(lcFontDb, "OpenType support missing for \"%ls\", script %d",
763 qUtf16Printable(def.families.constFirst()), script);
764 if (engine->ref.loadRelaxed() == 0)
765 delete engine;
766 return nullptr;
767 }
768
769 engine->isSmoothlyScalable = style->smoothScalable;
770 fontCache->insertEngine(key, engine);
771
772 if (Q_LIKELY(cacheForCommonScript && !engine->symbol)) {
773 // cache engine for Common script as well
774 key.script = QChar::Script_Common;
775 if (!fontCache->findEngine(key))
776 fontCache->insertEngine(key, engine);
777 }
778 }
779 }
780 return engine;
781}
782
783QFontEngine *QFontDatabasePrivate::loadEngine(int script, const QFontDef &request,
784 QtFontFamily *family, QtFontFoundry *foundry,
785 QtFontStyle *style, QtFontSize *size)
786{
787 QFontEngine *engine = loadSingleEngine(script, request, family, foundry, style, size);
788
789 if (engine && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) {
790 Q_TRACE(QFontDatabase_loadEngine, request.families.join(QLatin1Char(';')), request.pointSize);
791
792 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
793 QFontEngineMulti *pfMultiEngine = pfdb->fontEngineMulti(fontEngine: engine, script: QChar::Script(script));
794 if (!request.fallBackFamilies.isEmpty()) {
795 QStringList fallbacks = request.fallBackFamilies;
796
797 QFont::StyleHint styleHint = QFont::StyleHint(request.styleHint);
798 if (styleHint == QFont::AnyStyle && request.fixedPitch)
799 styleHint = QFont::TypeWriter;
800
801 fallbacks += fallbacksForFamily(family: family->name, style: QFont::Style(style->key.style), styleHint, script: QChar::Script(script));
802
803 pfMultiEngine->setFallbackFamiliesList(fallbacks);
804 }
805 engine = pfMultiEngine;
806
807 // Cache Multi font engine as well in case we got the single
808 // font engine when we are actually looking for a Multi one
809 QFontCache::Key key(request, script, 1);
810 QFontCache::instance()->insertEngine(key, engine);
811 }
812
813 return engine;
814}
815
816QtFontStyle::~QtFontStyle()
817{
818 while (count) {
819 // bitfield count-- in while condition does not work correctly in mwccsym2
820 count--;
821 QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration();
822 if (integration)
823 integration->fontDatabase()->releaseHandle(handle: pixelSizes[count].handle);
824 }
825
826 free(ptr: pixelSizes);
827}
828
829static QtFontStyle *bestStyle(QtFontFoundry *foundry, const QtFontStyle::Key &styleKey,
830 const QString &styleName = QString())
831{
832 int best = 0;
833 int dist = 0xffff;
834
835 for ( int i = 0; i < foundry->count; i++ ) {
836 QtFontStyle *style = foundry->styles[i];
837
838 if (!styleName.isEmpty() && styleName == style->styleName) {
839 dist = 0;
840 best = i;
841 break;
842 }
843
844 int d = qAbs( t: (int(styleKey.weight) - int(style->key.weight)) / 10 );
845
846 if ( styleKey.stretch != 0 && style->key.stretch != 0 ) {
847 d += qAbs( t: styleKey.stretch - style->key.stretch );
848 }
849
850 if (styleKey.style != style->key.style) {
851 if (styleKey.style != QFont::StyleNormal && style->key.style != QFont::StyleNormal)
852 // one is italic, the other oblique
853 d += 0x0001;
854 else
855 d += 0x1000;
856 }
857
858 if ( d < dist ) {
859 best = i;
860 dist = d;
861 }
862 }
863
864 qCDebug(lcFontMatch, " best style has distance 0x%x", dist );
865 return foundry->styles[best];
866}
867
868
869unsigned int QFontDatabasePrivate::bestFoundry(int script, unsigned int score, int styleStrategy,
870 const QtFontFamily *family, const QString &foundry_name,
871 QtFontStyle::Key styleKey, int pixelSize, char pitch,
872 QtFontDesc *desc, const QString &styleName)
873{
874 Q_UNUSED(script);
875 Q_UNUSED(pitch);
876
877 desc->foundry = nullptr;
878 desc->style = nullptr;
879 desc->size = nullptr;
880
881
882 qCDebug(lcFontMatch, " REMARK: looking for best foundry for family '%s' [%d]", family->name.toLatin1().constData(), family->count);
883
884 for (int x = 0; x < family->count; ++x) {
885 QtFontFoundry *foundry = family->foundries[x];
886 if (!foundry_name.isEmpty() && foundry->name.compare(s: foundry_name, cs: Qt::CaseInsensitive) != 0)
887 continue;
888
889 qCDebug(lcFontMatch, " looking for matching style in foundry '%s' %d",
890 foundry->name.isEmpty() ? "-- none --" : foundry->name.toLatin1().constData(), foundry->count);
891
892 QtFontStyle *style = bestStyle(foundry, styleKey, styleName);
893
894 if (!style->smoothScalable && (styleStrategy & QFont::ForceOutline)) {
895 qCDebug(lcFontMatch, " ForceOutline set, but not smoothly scalable");
896 continue;
897 }
898
899 int px = -1;
900 QtFontSize *size = nullptr;
901
902 // 1. see if we have an exact matching size
903 if (!(styleStrategy & QFont::ForceOutline)) {
904 size = style->pixelSize(size: pixelSize);
905 if (size) {
906 qCDebug(lcFontMatch, " found exact size match (%d pixels)", size->pixelSize);
907 px = size->pixelSize;
908 }
909 }
910
911 // 2. see if we have a smoothly scalable font
912 if (!size && style->smoothScalable && ! (styleStrategy & QFont::PreferBitmap)) {
913 size = style->pixelSize(SMOOTH_SCALABLE);
914 if (size) {
915 qCDebug(lcFontMatch, " found smoothly scalable font (%d pixels)", pixelSize);
916 px = pixelSize;
917 }
918 }
919
920 // 3. see if we have a bitmap scalable font
921 if (!size && style->bitmapScalable && (styleStrategy & QFont::PreferMatch)) {
922 size = style->pixelSize(size: 0);
923 if (size) {
924 qCDebug(lcFontMatch, " found bitmap scalable font (%d pixels)", pixelSize);
925 px = pixelSize;
926 }
927 }
928
929
930 // 4. find closest size match
931 if (! size) {
932 unsigned int distance = ~0u;
933 for (int x = 0; x < style->count; ++x) {
934
935 unsigned int d;
936 if (style->pixelSizes[x].pixelSize < pixelSize) {
937 // penalize sizes that are smaller than the
938 // requested size, due to truncation from floating
939 // point to integer conversions
940 d = pixelSize - style->pixelSizes[x].pixelSize + 1;
941 } else {
942 d = style->pixelSizes[x].pixelSize - pixelSize;
943 }
944
945 if (d < distance) {
946 distance = d;
947 size = style->pixelSizes + x;
948 qCDebug(lcFontMatch, " best size so far: %3d (%d)", size->pixelSize, pixelSize);
949 }
950 }
951
952 if (!size) {
953 qCDebug(lcFontMatch, " no size supports the script we want");
954 continue;
955 }
956
957 if (style->bitmapScalable && ! (styleStrategy & QFont::PreferQuality) &&
958 (distance * 10 / pixelSize) >= 2) {
959 // the closest size is not close enough, go ahead and
960 // use a bitmap scaled font
961 size = style->pixelSize(size: 0);
962 px = pixelSize;
963 } else {
964 px = size->pixelSize;
965 }
966 }
967
968
969 unsigned int this_score = 0x0000;
970 enum {
971 PitchMismatch = 0x4000,
972 StyleMismatch = 0x2000,
973 BitmapScaledPenalty = 0x1000
974 };
975 if (pitch != '*') {
976 if ((pitch == 'm' && !family->fixedPitch)
977 || (pitch == 'p' && family->fixedPitch))
978 this_score += PitchMismatch;
979 }
980 if (styleKey != style->key)
981 this_score += StyleMismatch;
982 if (!style->smoothScalable && px != size->pixelSize) // bitmap scaled
983 this_score += BitmapScaledPenalty;
984 if (px != pixelSize) // close, but not exact, size match
985 this_score += qAbs(t: px - pixelSize);
986
987 if (this_score < score) {
988 qCDebug(lcFontMatch, " found a match: score %x best score so far %x",
989 this_score, score);
990
991 score = this_score;
992 desc->foundry = foundry;
993 desc->style = style;
994 desc->size = size;
995 } else {
996 qCDebug(lcFontMatch, " score %x no better than best %x", this_score, score);
997 }
998 }
999
1000 return score;
1001}
1002
1003static bool matchFamilyName(const QString &familyName, QtFontFamily *f)
1004{
1005 if (familyName.isEmpty())
1006 return true;
1007 return f->matchesFamilyName(familyName);
1008}
1009
1010/*!
1011 \internal
1012
1013 Tries to find the best match for a given request and family/foundry
1014*/
1015int QFontDatabasePrivate::match(int script, const QFontDef &request, const QString &family_name,
1016 const QString &foundry_name, QtFontDesc *desc, const QList<int> &blacklistedFamilies,
1017 unsigned int *resultingScore)
1018{
1019 int result = -1;
1020
1021 QtFontStyle::Key styleKey;
1022 styleKey.style = request.style;
1023 styleKey.weight = request.weight;
1024 // Prefer a stretch closest to 100.
1025 styleKey.stretch = request.stretch ? request.stretch : 100;
1026 char pitch = request.ignorePitch ? '*' : request.fixedPitch ? 'm' : 'p';
1027
1028
1029 qCDebug(lcFontMatch, "QFontDatabasePrivate::match\n"
1030 " request:\n"
1031 " family: %s [%s], script: %d\n"
1032 " styleName: %s\n"
1033 " weight: %d, style: %d\n"
1034 " stretch: %d\n"
1035 " pixelSize: %g\n"
1036 " pitch: %c",
1037 family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(),
1038 foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(), script,
1039 request.styleName.isEmpty() ? "-- any --" : request.styleName.toLatin1().constData(),
1040 request.weight, request.style, request.stretch, request.pixelSize, pitch);
1041
1042 desc->family = nullptr;
1043 desc->foundry = nullptr;
1044 desc->style = nullptr;
1045 desc->size = nullptr;
1046
1047 unsigned int score = ~0u;
1048
1049 QMutexLocker locker(fontDatabaseMutex());
1050 QFontDatabasePrivate::ensureFontDatabase();
1051
1052 auto writingSystem = qt_writing_system_for_script(script);
1053 if (writingSystem >= QFontDatabase::WritingSystemsCount)
1054 writingSystem = QFontDatabase::Any;
1055
1056 auto *db = QFontDatabasePrivate::instance();
1057 for (int x = 0; x < db->count; ++x) {
1058 if (blacklistedFamilies.contains(t: x))
1059 continue;
1060 QtFontDesc test;
1061 test.family = db->families[x];
1062
1063 if (!matchFamilyName(familyName: family_name, f: test.family))
1064 continue;
1065 if (!test.family->ensurePopulated())
1066 continue;
1067
1068 // Check if family is supported in the script we want
1069 if (writingSystem != QFontDatabase::Any && !familySupportsWritingSystem(family: test.family, writingSystem))
1070 continue;
1071
1072 // as we know the script is supported, we can be sure
1073 // to find a matching font here.
1074 unsigned int newscore =
1075 bestFoundry(script, score, styleStrategy: request.styleStrategy,
1076 family: test.family, foundry_name, styleKey, pixelSize: request.pixelSize, pitch,
1077 desc: &test, styleName: request.styleName);
1078 if (test.foundry == nullptr && !foundry_name.isEmpty()) {
1079 // the specific foundry was not found, so look for
1080 // any foundry matching our requirements
1081 newscore = bestFoundry(script, score, styleStrategy: request.styleStrategy, family: test.family,
1082 foundry_name: QString(), styleKey, pixelSize: request.pixelSize,
1083 pitch, desc: &test, styleName: request.styleName);
1084 }
1085
1086 if (newscore < score) {
1087 result = x;
1088 score = newscore;
1089 *desc = test;
1090 }
1091 if (newscore < 10) // xlfd instead of FT... just accept it
1092 break;
1093 }
1094
1095 if (resultingScore != nullptr)
1096 *resultingScore = score;
1097
1098 return result;
1099}
1100
1101static QString styleStringHelper(int weight, QFont::Style style)
1102{
1103 QString result;
1104 if (weight > QFont::Normal) {
1105 if (weight >= QFont::Black)
1106 result = QCoreApplication::translate(context: "QFontDatabase", key: "Black");
1107 else if (weight >= QFont::ExtraBold)
1108 result = QCoreApplication::translate(context: "QFontDatabase", key: "Extra Bold");
1109 else if (weight >= QFont::Bold)
1110 result = QCoreApplication::translate(context: "QFontDatabase", key: "Bold");
1111 else if (weight >= QFont::DemiBold)
1112 result = QCoreApplication::translate(context: "QFontDatabase", key: "Demi Bold");
1113 else if (weight >= QFont::Medium)
1114 result = QCoreApplication::translate(context: "QFontDatabase", key: "Medium", disambiguation: "The Medium font weight");
1115 } else {
1116 if (weight <= QFont::Thin)
1117 result = QCoreApplication::translate(context: "QFontDatabase", key: "Thin");
1118 else if (weight <= QFont::ExtraLight)
1119 result = QCoreApplication::translate(context: "QFontDatabase", key: "Extra Light");
1120 else if (weight <= QFont::Light)
1121 result = QCoreApplication::translate(context: "QFontDatabase", key: "Light");
1122 }
1123
1124 if (style == QFont::StyleItalic)
1125 result += u' ' + QCoreApplication::translate(context: "QFontDatabase", key: "Italic");
1126 else if (style == QFont::StyleOblique)
1127 result += u' ' + QCoreApplication::translate(context: "QFontDatabase", key: "Oblique");
1128
1129 if (result.isEmpty())
1130 result = QCoreApplication::translate(context: "QFontDatabase", key: "Normal", disambiguation: "The Normal or Regular font weight");
1131
1132 return result.simplified();
1133}
1134
1135/*!
1136 Returns a string that describes the style of the \a font. For
1137 example, "Bold Italic", "Bold", "Italic" or "Normal". An empty
1138 string may be returned.
1139*/
1140QString QFontDatabase::styleString(const QFont &font)
1141{
1142 return font.styleName().isEmpty() ? styleStringHelper(weight: font.weight(), style: font.style())
1143 : font.styleName();
1144}
1145
1146/*!
1147 Returns a string that describes the style of the \a fontInfo. For
1148 example, "Bold Italic", "Bold", "Italic" or "Normal". An empty
1149 string may be returned.
1150*/
1151QString QFontDatabase::styleString(const QFontInfo &fontInfo)
1152{
1153 return fontInfo.styleName().isEmpty() ? styleStringHelper(weight: fontInfo.weight(), style: fontInfo.style())
1154 : fontInfo.styleName();
1155}
1156
1157
1158/*!
1159 \class QFontDatabase
1160 \threadsafe
1161 \inmodule QtGui
1162
1163 \brief The QFontDatabase class provides information about the fonts available in the underlying window system.
1164
1165 \ingroup appearance
1166
1167 The most common uses of this class are to query the database for
1168 the list of font families() and for the pointSizes() and styles()
1169 that are available for each family. An alternative to pointSizes()
1170 is smoothSizes() which returns the sizes at which a given family
1171 and style will look attractive.
1172
1173 If the font family is available from two or more foundries the
1174 foundry name is included in the family name; for example:
1175 "Helvetica [Adobe]" and "Helvetica [Cronyx]". When you specify a
1176 family, you can either use the old hyphenated "foundry-family"
1177 format or the bracketed "family [foundry]" format; for example:
1178 "Cronyx-Helvetica" or "Helvetica [Cronyx]". If the family has a
1179 foundry it is always returned using the bracketed format, as is
1180 the case with the value returned by families().
1181
1182 The font() function returns a QFont given a family, style and
1183 point size.
1184
1185 A family and style combination can be checked to see if it is
1186 italic() or bold(), and to retrieve its weight(). Similarly we can
1187 call isBitmapScalable(), isSmoothlyScalable(), isScalable() and
1188 isFixedPitch().
1189
1190 Use the styleString() to obtain a text version of a style.
1191
1192 The QFontDatabase class provides some helper functions, for
1193 example, standardSizes(). You can retrieve the description of a
1194 writing system using writingSystemName(), and a sample of
1195 characters in a writing system with writingSystemSample().
1196
1197 Example:
1198
1199 \snippet qfontdatabase/qfontdatabase_snippets.cpp 0
1200
1201 This example gets the list of font families, the list of
1202 styles for each family, and the point sizes that are available for
1203 each combination of family and style, displaying this information
1204 in a tree view.
1205
1206 \sa QFont, QFontInfo, QFontMetrics
1207*/
1208
1209/*!
1210 \fn QFontDatabase::QFontDatabase()
1211 \deprecated [6.0] Call the class methods as static functions instead.
1212
1213 Creates a font database object.
1214*/
1215
1216/*!
1217 \enum QFontDatabase::WritingSystem
1218
1219 \value Any
1220 \value Latin
1221 \value Greek
1222 \value Cyrillic
1223 \value Armenian
1224 \value Hebrew
1225 \value Arabic
1226 \value Syriac
1227 \value Thaana
1228 \value Devanagari
1229 \value Bengali
1230 \value Gurmukhi
1231 \value Gujarati
1232 \value Oriya
1233 \value Tamil
1234 \value Telugu
1235 \value Kannada
1236 \value Malayalam
1237 \value Sinhala
1238 \value Thai
1239 \value Lao
1240 \value Tibetan
1241 \value Myanmar
1242 \value Georgian
1243 \value Khmer
1244 \value SimplifiedChinese
1245 \value TraditionalChinese
1246 \value Japanese
1247 \value Korean
1248 \value Vietnamese
1249 \value Symbol
1250 \value Other (the same as Symbol)
1251 \value Ogham
1252 \value Runic
1253 \value Nko
1254
1255 \omitvalue WritingSystemsCount
1256*/
1257
1258/*!
1259 \enum QFontDatabase::SystemFont
1260
1261 \value GeneralFont The default system font.
1262 \value FixedFont The fixed font that the system recommends.
1263 \value TitleFont The system standard font for titles.
1264 \value SmallestReadableFont The smallest readable system font.
1265
1266 \since 5.2
1267*/
1268
1269/*!
1270 \class QFontDatabasePrivate
1271 \internal
1272
1273 Singleton implementation of the public QFontDatabase APIs,
1274 accessed through QFontDatabasePrivate::instance().
1275
1276 The database is organized in multiple levels:
1277
1278 - QFontDatabasePrivate::families
1279 - QtFontFamily::foundries
1280 - QtFontFoundry::styles
1281 - QtFontStyle::sizes
1282 - QtFontSize::pixelSize
1283
1284 The font database is the single source of truth when doing
1285 font matching, so the database must be sufficiently filled
1286 before attempting a match.
1287
1288 The database is populated (filled) from two sources:
1289
1290 1. The system (platform's) view of the available fonts
1291
1292 Initiated via QFontDatabasePrivate::populateFontDatabase().
1293
1294 a. Can be registered lazily by family only, by calling
1295 QPlatformFontDatabase::registerFontFamily(), and later
1296 populated via QPlatformFontDatabase::populateFamily().
1297
1298 b. Or fully registered with all styles, by calling
1299 QPlatformFontDatabase::registerFont().
1300
1301 2. The fonts registered by the application via Qt APIs
1302
1303 Initiated via QFontDatabase::addApplicationFont() and
1304 QFontDatabase::addApplicationFontFromData().
1305
1306 Application fonts are always fully registered when added.
1307
1308 Fonts can be added at any time, so the database may grow even
1309 after QFontDatabasePrivate::populateFontDatabase() has been
1310 completed.
1311
1312 The database does not support granular removal of fonts,
1313 so if the system fonts change, or an application font is
1314 removed, the font database will be cleared and then filled
1315 from scratch, via QFontDatabasePrivate:invalidate() and
1316 QFontDatabasePrivate::ensureFontDatabase().
1317*/
1318
1319/*!
1320 \internal
1321
1322 Initializes the font database if necessary and returns its
1323 pointer. Mutex lock must be held when calling this function.
1324*/
1325QFontDatabasePrivate *QFontDatabasePrivate::ensureFontDatabase()
1326{
1327 auto *d = QFontDatabasePrivate::instance();
1328 if (!d->populated) {
1329 // The font database may have been partially populated, but to ensure
1330 // we can answer queries for any platform- or user-provided family we
1331 // need to fully populate it now.
1332 qCDebug(lcFontDb) << "Populating font database";
1333
1334 if (Q_UNLIKELY(qGuiApp == nullptr || QGuiApplicationPrivate::platformIntegration() == nullptr))
1335 qFatal(msg: "QFontDatabase: Must construct a QGuiApplication before accessing QFontDatabase");
1336
1337 auto *platformFontDatabase = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
1338 platformFontDatabase->populateFontDatabase();
1339
1340 for (int i = 0; i < d->applicationFonts.size(); i++) {
1341 auto *font = &d->applicationFonts[i];
1342 if (!font->isNull() && !font->isPopulated())
1343 platformFontDatabase->addApplicationFont(fontData: font->data, fileName: font->fileName, font);
1344 }
1345
1346 // Note: Both application fonts and platform fonts may be added
1347 // after this initial population, so the only thing we are tracking
1348 // is whether we've done our part in ensuring a filled font database.
1349 d->populated = true;
1350 }
1351 return d;
1352}
1353
1354/*!
1355 Returns a sorted list of the available writing systems. This is
1356 list generated from information about all installed fonts on the
1357 system.
1358
1359 \sa families()
1360*/
1361QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems()
1362{
1363 QMutexLocker locker(fontDatabaseMutex());
1364 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1365
1366 quint64 writingSystemsFound = 0;
1367 static_assert(WritingSystemsCount < 64);
1368
1369 for (int i = 0; i < d->count; ++i) {
1370 QtFontFamily *family = d->families[i];
1371 if (!family->ensurePopulated())
1372 continue;
1373
1374 if (family->count == 0)
1375 continue;
1376 for (uint x = Latin; x < uint(WritingSystemsCount); ++x) {
1377 if (family->writingSystems[x] & QtFontFamily::Supported)
1378 writingSystemsFound |= quint64(1) << x;
1379 }
1380 }
1381
1382 // mutex protection no longer needed - just working on local data now:
1383 locker.unlock();
1384
1385 QList<WritingSystem> list;
1386 list.reserve(asize: qPopulationCount(v: writingSystemsFound));
1387 for (uint x = Latin ; x < uint(WritingSystemsCount); ++x) {
1388 if (writingSystemsFound & (quint64(1) << x))
1389 list.push_back(t: WritingSystem(x));
1390 }
1391 return list;
1392}
1393
1394
1395/*!
1396 Returns a sorted list of the writing systems supported by a given
1397 font \a family.
1398
1399 \sa families()
1400*/
1401QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems(const QString &family)
1402{
1403 QString familyName, foundryName;
1404 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1405
1406 QMutexLocker locker(fontDatabaseMutex());
1407 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1408
1409 QList<WritingSystem> list;
1410 QtFontFamily *f = d->family(f: familyName);
1411 if (!f || f->count == 0)
1412 return list;
1413
1414 for (int x = Latin; x < WritingSystemsCount; ++x) {
1415 const WritingSystem writingSystem = WritingSystem(x);
1416 if (f->writingSystems[writingSystem] & QtFontFamily::Supported)
1417 list.append(t: writingSystem);
1418 }
1419 return list;
1420}
1421
1422
1423/*!
1424 Returns a sorted list of the available font families which support
1425 the \a writingSystem.
1426
1427 If a family exists in several foundries, the returned name for
1428 that font is in the form "family [foundry]". Examples: "Times
1429 [Adobe]", "Times [Cronyx]", "Palatino".
1430
1431 \sa writingSystems()
1432*/
1433QStringList QFontDatabase::families(WritingSystem writingSystem)
1434{
1435 QMutexLocker locker(fontDatabaseMutex());
1436 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1437
1438 QStringList flist;
1439 for (int i = 0; i < d->count; i++) {
1440 QtFontFamily *f = d->families[i];
1441 if (f->populated && f->count == 0)
1442 continue;
1443 if (writingSystem != Any) {
1444 if (!f->ensurePopulated())
1445 continue;
1446 if (f->writingSystems[writingSystem] != QtFontFamily::Supported)
1447 continue;
1448 }
1449 if (!f->populated || f->count == 1) {
1450 flist.append(t: f->name);
1451 } else {
1452 for (int j = 0; j < f->count; j++) {
1453 QString str = f->name;
1454 QString foundry = f->foundries[j]->name;
1455 if (!foundry.isEmpty()) {
1456 str += " ["_L1;
1457 str += foundry;
1458 str += u']';
1459 }
1460 flist.append(t: str);
1461 }
1462 }
1463 }
1464 return flist;
1465}
1466
1467/*!
1468 Returns a list of the styles available for the font family \a
1469 family. Some example styles: "Light", "Light Italic", "Bold",
1470 "Oblique", "Demi". The list may be empty.
1471
1472 \sa families()
1473*/
1474QStringList QFontDatabase::styles(const QString &family)
1475{
1476 QString familyName, foundryName;
1477 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1478
1479 QMutexLocker locker(fontDatabaseMutex());
1480 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1481
1482 QStringList l;
1483 QtFontFamily *f = d->family(f: familyName);
1484 if (!f)
1485 return l;
1486
1487 QtFontFoundry allStyles(foundryName);
1488 for (int j = 0; j < f->count; j++) {
1489 QtFontFoundry *foundry = f->foundries[j];
1490 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1491 for (int k = 0; k < foundry->count; k++) {
1492 QtFontStyle::Key ke(foundry->styles[k]->key);
1493 ke.stretch = 0;
1494 allStyles.style(key: ke, styleName: foundry->styles[k]->styleName, create: true);
1495 }
1496 }
1497 }
1498
1499 l.reserve(asize: allStyles.count);
1500 for (int i = 0; i < allStyles.count; i++) {
1501 l.append(t: allStyles.styles[i]->styleName.isEmpty() ?
1502 styleStringHelper(weight: allStyles.styles[i]->key.weight,
1503 style: (QFont::Style)allStyles.styles[i]->key.style) :
1504 allStyles.styles[i]->styleName);
1505 }
1506 return l;
1507}
1508
1509/*!
1510 Returns \c true if the font that has family \a family and style \a
1511 style is fixed pitch; otherwise returns \c false.
1512*/
1513
1514bool QFontDatabase::isFixedPitch(const QString &family,
1515 const QString &style)
1516{
1517 Q_UNUSED(style);
1518
1519 QString familyName, foundryName;
1520 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1521
1522 QMutexLocker locker(fontDatabaseMutex());
1523 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1524
1525 QtFontFamily *f = d->family(f: familyName);
1526 return (f && f->fixedPitch);
1527}
1528
1529/*!
1530 Returns \c true if the font that has family \a family and style \a
1531 style is a scalable bitmap font; otherwise returns \c false. Scaling
1532 a bitmap font usually produces an unattractive hardly readable
1533 result, because the pixels of the font are scaled. If you need to
1534 scale a bitmap font it is better to scale it to one of the fixed
1535 sizes returned by smoothSizes().
1536
1537 \sa isScalable(), isSmoothlyScalable()
1538*/
1539bool QFontDatabase::isBitmapScalable(const QString &family,
1540 const QString &style)
1541{
1542 bool bitmapScalable = false;
1543 QString familyName, foundryName;
1544 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1545
1546 QMutexLocker locker(fontDatabaseMutex());
1547 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1548
1549 QtFontFamily *f = d->family(f: familyName);
1550 if (!f) return bitmapScalable;
1551
1552 QtFontStyle::Key styleKey(style);
1553 for (int j = 0; j < f->count; j++) {
1554 QtFontFoundry *foundry = f->foundries[j];
1555 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1556 for (int k = 0; k < foundry->count; k++)
1557 if ((style.isEmpty() ||
1558 foundry->styles[k]->styleName == style ||
1559 foundry->styles[k]->key == styleKey)
1560 && foundry->styles[k]->bitmapScalable && !foundry->styles[k]->smoothScalable) {
1561 bitmapScalable = true;
1562 goto end;
1563 }
1564 }
1565 }
1566 end:
1567 return bitmapScalable;
1568}
1569
1570
1571/*!
1572 Returns \c true if the font that has family \a family and style \a
1573 style is smoothly scalable; otherwise returns \c false. If this
1574 function returns \c true, it's safe to scale this font to any size,
1575 and the result will always look attractive.
1576
1577 \sa isScalable(), isBitmapScalable()
1578*/
1579bool QFontDatabase::isSmoothlyScalable(const QString &family, const QString &style)
1580{
1581 bool smoothScalable = false;
1582 QString familyName, foundryName;
1583 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1584
1585 QMutexLocker locker(fontDatabaseMutex());
1586 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1587
1588 QtFontFamily *f = d->family(f: familyName);
1589 if (!f) {
1590 for (int i = 0; i < d->count; i++) {
1591 if (d->families[i]->matchesFamilyName(familyName)) {
1592 f = d->families[i];
1593 if (f->ensurePopulated())
1594 break;
1595 }
1596 }
1597 }
1598 if (!f) return smoothScalable;
1599
1600 const QtFontStyle::Key styleKey(style);
1601 for (int j = 0; j < f->count; j++) {
1602 QtFontFoundry *foundry = f->foundries[j];
1603 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1604 for (int k = 0; k < foundry->count; k++) {
1605 const QtFontStyle *fontStyle = foundry->styles[k];
1606 smoothScalable =
1607 fontStyle->smoothScalable
1608 && ((style.isEmpty()
1609 || fontStyle->styleName == style
1610 || fontStyle->key == styleKey)
1611 || (fontStyle->styleName.isEmpty()
1612 && style == styleStringHelper(weight: fontStyle->key.weight,
1613 style: QFont::Style(fontStyle->key.style))));
1614 if (smoothScalable)
1615 goto end;
1616 }
1617 }
1618 }
1619 end:
1620 return smoothScalable;
1621}
1622
1623/*!
1624 Returns \c true if the font that has family \a family and style \a
1625 style is scalable; otherwise returns \c false.
1626
1627 \sa isBitmapScalable(), isSmoothlyScalable()
1628*/
1629bool QFontDatabase::isScalable(const QString &family,
1630 const QString &style)
1631{
1632 QMutexLocker locker(fontDatabaseMutex());
1633 if (isSmoothlyScalable(family, style))
1634 return true;
1635 return isBitmapScalable(family, style);
1636}
1637
1638
1639/*!
1640 Returns a list of the point sizes available for the font that has
1641 family \a family and style \a styleName. The list may be empty.
1642
1643 \sa smoothSizes(), standardSizes()
1644*/
1645QList<int> QFontDatabase::pointSizes(const QString &family,
1646 const QString &styleName)
1647{
1648 if (QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fontsAlwaysScalable())
1649 return standardSizes();
1650
1651 bool smoothScalable = false;
1652 QString familyName, foundryName;
1653 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1654
1655 QMutexLocker locker(fontDatabaseMutex());
1656 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1657
1658 QList<int> sizes;
1659
1660 QtFontFamily *fam = d->family(f: familyName);
1661 if (!fam) return sizes;
1662
1663
1664 const int dpi = qt_defaultDpiY(); // embedded
1665
1666 QtFontStyle::Key styleKey(styleName);
1667 for (int j = 0; j < fam->count; j++) {
1668 QtFontFoundry *foundry = fam->foundries[j];
1669 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1670 QtFontStyle *style = foundry->style(key: styleKey, styleName);
1671 if (!style) continue;
1672
1673 if (style->smoothScalable) {
1674 smoothScalable = true;
1675 goto end;
1676 }
1677 for (int l = 0; l < style->count; l++) {
1678 const QtFontSize *size = style->pixelSizes + l;
1679
1680 if (size->pixelSize != 0 && size->pixelSize != SMOOTH_SCALABLE) {
1681 const int pointSize = qRound(d: size->pixelSize * 72.0 / dpi);
1682 if (! sizes.contains(t: pointSize))
1683 sizes.append(t: pointSize);
1684 }
1685 }
1686 }
1687 }
1688 end:
1689 if (smoothScalable)
1690 return standardSizes();
1691
1692 std::sort(first: sizes.begin(), last: sizes.end());
1693 return sizes;
1694}
1695
1696/*!
1697 Returns a QFont object that has family \a family, style \a style
1698 and point size \a pointSize. If no matching font could be created,
1699 a QFont object that uses the application's default font is
1700 returned.
1701*/
1702QFont QFontDatabase::font(const QString &family, const QString &style,
1703 int pointSize)
1704{
1705 QString familyName, foundryName;
1706 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1707 QMutexLocker locker(fontDatabaseMutex());
1708 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1709
1710 QtFontFoundry allStyles(foundryName);
1711 QtFontFamily *f = d->family(f: familyName);
1712 if (!f) return QGuiApplication::font();
1713
1714 for (int j = 0; j < f->count; j++) {
1715 QtFontFoundry *foundry = f->foundries[j];
1716 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1717 for (int k = 0; k < foundry->count; k++)
1718 allStyles.style(key: foundry->styles[k]->key, styleName: foundry->styles[k]->styleName, create: true);
1719 }
1720 }
1721
1722 QtFontStyle::Key styleKey(style);
1723 QtFontStyle *s = bestStyle(foundry: &allStyles, styleKey, styleName: style);
1724
1725 if (!s) // no styles found?
1726 return QGuiApplication::font();
1727
1728 QFont fnt(QStringList{family}, pointSize, s->key.weight);
1729 fnt.setStyle((QFont::Style)s->key.style);
1730 if (!s->styleName.isEmpty())
1731 fnt.setStyleName(s->styleName);
1732 return fnt;
1733}
1734
1735
1736/*!
1737 Returns the point sizes of a font that has family \a family and
1738 style \a styleName that will look attractive. The list may be empty.
1739 For non-scalable fonts and bitmap scalable fonts, this function
1740 is equivalent to pointSizes().
1741
1742 \sa pointSizes(), standardSizes()
1743*/
1744QList<int> QFontDatabase::smoothSizes(const QString &family,
1745 const QString &styleName)
1746{
1747 if (QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fontsAlwaysScalable())
1748 return standardSizes();
1749
1750 bool smoothScalable = false;
1751 QString familyName, foundryName;
1752 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1753
1754 QMutexLocker locker(fontDatabaseMutex());
1755 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1756
1757 QList<int> sizes;
1758
1759 QtFontFamily *fam = d->family(f: familyName);
1760 if (!fam)
1761 return sizes;
1762
1763 const int dpi = qt_defaultDpiY(); // embedded
1764
1765 QtFontStyle::Key styleKey(styleName);
1766 for (int j = 0; j < fam->count; j++) {
1767 QtFontFoundry *foundry = fam->foundries[j];
1768 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1769 QtFontStyle *style = foundry->style(key: styleKey, styleName);
1770 if (!style) continue;
1771
1772 if (style->smoothScalable) {
1773 smoothScalable = true;
1774 goto end;
1775 }
1776 for (int l = 0; l < style->count; l++) {
1777 const QtFontSize *size = style->pixelSizes + l;
1778
1779 if (size->pixelSize != 0 && size->pixelSize != SMOOTH_SCALABLE) {
1780 const int pointSize = qRound(d: size->pixelSize * 72.0 / dpi);
1781 if (! sizes.contains(t: pointSize))
1782 sizes.append(t: pointSize);
1783 }
1784 }
1785 }
1786 }
1787 end:
1788 if (smoothScalable)
1789 return QFontDatabase::standardSizes();
1790
1791 std::sort(first: sizes.begin(), last: sizes.end());
1792 return sizes;
1793}
1794
1795
1796/*!
1797 Returns a list of standard font sizes.
1798
1799 \sa smoothSizes(), pointSizes()
1800*/
1801QList<int> QFontDatabase::standardSizes()
1802{
1803 return QGuiApplicationPrivate::platformIntegration()->fontDatabase()->standardSizes();
1804}
1805
1806
1807/*!
1808 Returns \c true if the font that has family \a family and style \a
1809 style is italic; otherwise returns \c false.
1810
1811 \sa weight(), bold()
1812*/
1813bool QFontDatabase::italic(const QString &family, const QString &style)
1814{
1815 QString familyName, foundryName;
1816 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1817
1818 QMutexLocker locker(fontDatabaseMutex());
1819 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1820
1821 QtFontFoundry allStyles(foundryName);
1822 QtFontFamily *f = d->family(f: familyName);
1823 if (!f) return false;
1824
1825 for (int j = 0; j < f->count; j++) {
1826 QtFontFoundry *foundry = f->foundries[j];
1827 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1828 for (int k = 0; k < foundry->count; k++)
1829 allStyles.style(key: foundry->styles[k]->key, styleName: foundry->styles[k]->styleName, create: true);
1830 }
1831 }
1832
1833 QtFontStyle::Key styleKey(style);
1834 QtFontStyle *s = allStyles.style(key: styleKey, styleName: style);
1835 return s && s->key.style == QFont::StyleItalic;
1836}
1837
1838
1839/*!
1840 Returns \c true if the font that has family \a family and style \a
1841 style is bold; otherwise returns \c false.
1842
1843 \sa italic(), weight()
1844*/
1845bool QFontDatabase::bold(const QString &family,
1846 const QString &style)
1847{
1848 QString familyName, foundryName;
1849 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1850
1851 QMutexLocker locker(fontDatabaseMutex());
1852 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1853
1854 QtFontFoundry allStyles(foundryName);
1855 QtFontFamily *f = d->family(f: familyName);
1856 if (!f) return false;
1857
1858 for (int j = 0; j < f->count; j++) {
1859 QtFontFoundry *foundry = f->foundries[j];
1860 if (foundryName.isEmpty() ||
1861 foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1862 for (int k = 0; k < foundry->count; k++)
1863 allStyles.style(key: foundry->styles[k]->key, styleName: foundry->styles[k]->styleName, create: true);
1864 }
1865 }
1866
1867 QtFontStyle::Key styleKey(style);
1868 QtFontStyle *s = allStyles.style(key: styleKey, styleName: style);
1869 return s && s->key.weight >= QFont::Bold;
1870}
1871
1872
1873/*!
1874 Returns the weight of the font that has family \a family and style
1875 \a style. If there is no such family and style combination,
1876 returns -1.
1877
1878 \sa italic(), bold()
1879*/
1880int QFontDatabase::weight(const QString &family,
1881 const QString &style)
1882{
1883 QString familyName, foundryName;
1884 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1885
1886 QMutexLocker locker(fontDatabaseMutex());
1887 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1888
1889 QtFontFoundry allStyles(foundryName);
1890 QtFontFamily *f = d->family(f: familyName);
1891 if (!f) return -1;
1892
1893 for (int j = 0; j < f->count; j++) {
1894 QtFontFoundry *foundry = f->foundries[j];
1895 if (foundryName.isEmpty() ||
1896 foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1897 for (int k = 0; k < foundry->count; k++)
1898 allStyles.style(key: foundry->styles[k]->key, styleName: foundry->styles[k]->styleName, create: true);
1899 }
1900 }
1901
1902 QtFontStyle::Key styleKey(style);
1903 QtFontStyle *s = allStyles.style(key: styleKey, styleName: style);
1904 return s ? s->key.weight : -1;
1905}
1906
1907
1908/*! \internal */
1909bool QFontDatabase::hasFamily(const QString &family)
1910{
1911 QString parsedFamily, foundry;
1912 parseFontName(name: family, foundry, family&: parsedFamily);
1913 const QString familyAlias = QFontDatabasePrivate::resolveFontFamilyAlias(family: parsedFamily);
1914
1915 QMutexLocker locker(fontDatabaseMutex());
1916 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1917
1918 for (int i = 0; i < d->count; i++) {
1919 QtFontFamily *f = d->families[i];
1920 if (f->populated && f->count == 0)
1921 continue;
1922 if (familyAlias.compare(s: f->name, cs: Qt::CaseInsensitive) == 0)
1923 return true;
1924 }
1925
1926 return false;
1927}
1928
1929
1930/*!
1931 \since 5.5
1932
1933 Returns \c true if and only if the \a family font family is private.
1934
1935 This happens, for instance, on \macos and iOS, where the system UI fonts are not
1936 accessible to the user. For completeness, QFontDatabase::families() returns all
1937 font families, including the private ones. You should use this function if you
1938 are developing a font selection control in order to keep private fonts hidden.
1939
1940 \sa families()
1941*/
1942bool QFontDatabase::isPrivateFamily(const QString &family)
1943{
1944 return QGuiApplicationPrivate::platformIntegration()->fontDatabase()->isPrivateFontFamily(family);
1945}
1946
1947
1948/*!
1949 Returns the names the \a writingSystem (e.g. for displaying to the
1950 user in a dialog).
1951*/
1952QString QFontDatabase::writingSystemName(WritingSystem writingSystem)
1953{
1954 const char *name = nullptr;
1955 switch (writingSystem) {
1956 case Any:
1957 name = QT_TRANSLATE_NOOP("QFontDatabase", "Any");
1958 break;
1959 case Latin:
1960 name = QT_TRANSLATE_NOOP("QFontDatabase", "Latin");
1961 break;
1962 case Greek:
1963 name = QT_TRANSLATE_NOOP("QFontDatabase", "Greek");
1964 break;
1965 case Cyrillic:
1966 name = QT_TRANSLATE_NOOP("QFontDatabase", "Cyrillic");
1967 break;
1968 case Armenian:
1969 name = QT_TRANSLATE_NOOP("QFontDatabase", "Armenian");
1970 break;
1971 case Hebrew:
1972 name = QT_TRANSLATE_NOOP("QFontDatabase", "Hebrew");
1973 break;
1974 case Arabic:
1975 name = QT_TRANSLATE_NOOP("QFontDatabase", "Arabic");
1976 break;
1977 case Syriac:
1978 name = QT_TRANSLATE_NOOP("QFontDatabase", "Syriac");
1979 break;
1980 case Thaana:
1981 name = QT_TRANSLATE_NOOP("QFontDatabase", "Thaana");
1982 break;
1983 case Devanagari:
1984 name = QT_TRANSLATE_NOOP("QFontDatabase", "Devanagari");
1985 break;
1986 case Bengali:
1987 name = QT_TRANSLATE_NOOP("QFontDatabase", "Bengali");
1988 break;
1989 case Gurmukhi:
1990 name = QT_TRANSLATE_NOOP("QFontDatabase", "Gurmukhi");
1991 break;
1992 case Gujarati:
1993 name = QT_TRANSLATE_NOOP("QFontDatabase", "Gujarati");
1994 break;
1995 case Oriya:
1996 name = QT_TRANSLATE_NOOP("QFontDatabase", "Oriya");
1997 break;
1998 case Tamil:
1999 name = QT_TRANSLATE_NOOP("QFontDatabase", "Tamil");
2000 break;
2001 case Telugu:
2002 name = QT_TRANSLATE_NOOP("QFontDatabase", "Telugu");
2003 break;
2004 case Kannada:
2005 name = QT_TRANSLATE_NOOP("QFontDatabase", "Kannada");
2006 break;
2007 case Malayalam:
2008 name = QT_TRANSLATE_NOOP("QFontDatabase", "Malayalam");
2009 break;
2010 case Sinhala:
2011 name = QT_TRANSLATE_NOOP("QFontDatabase", "Sinhala");
2012 break;
2013 case Thai:
2014 name = QT_TRANSLATE_NOOP("QFontDatabase", "Thai");
2015 break;
2016 case Lao:
2017 name = QT_TRANSLATE_NOOP("QFontDatabase", "Lao");
2018 break;
2019 case Tibetan:
2020 name = QT_TRANSLATE_NOOP("QFontDatabase", "Tibetan");
2021 break;
2022 case Myanmar:
2023 name = QT_TRANSLATE_NOOP("QFontDatabase", "Myanmar");
2024 break;
2025 case Georgian:
2026 name = QT_TRANSLATE_NOOP("QFontDatabase", "Georgian");
2027 break;
2028 case Khmer:
2029 name = QT_TRANSLATE_NOOP("QFontDatabase", "Khmer");
2030 break;
2031 case SimplifiedChinese:
2032 name = QT_TRANSLATE_NOOP("QFontDatabase", "Simplified Chinese");
2033 break;
2034 case TraditionalChinese:
2035 name = QT_TRANSLATE_NOOP("QFontDatabase", "Traditional Chinese");
2036 break;
2037 case Japanese:
2038 name = QT_TRANSLATE_NOOP("QFontDatabase", "Japanese");
2039 break;
2040 case Korean:
2041 name = QT_TRANSLATE_NOOP("QFontDatabase", "Korean");
2042 break;
2043 case Vietnamese:
2044 name = QT_TRANSLATE_NOOP("QFontDatabase", "Vietnamese");
2045 break;
2046 case Symbol:
2047 name = QT_TRANSLATE_NOOP("QFontDatabase", "Symbol");
2048 break;
2049 case Ogham:
2050 name = QT_TRANSLATE_NOOP("QFontDatabase", "Ogham");
2051 break;
2052 case Runic:
2053 name = QT_TRANSLATE_NOOP("QFontDatabase", "Runic");
2054 break;
2055 case Nko:
2056 name = QT_TRANSLATE_NOOP("QFontDatabase", "N'Ko");
2057 break;
2058 default:
2059 Q_ASSERT_X(false, "QFontDatabase::writingSystemName", "invalid 'writingSystem' parameter");
2060 break;
2061 }
2062 return QCoreApplication::translate(context: "QFontDatabase", key: name);
2063}
2064
2065/*!
2066 Returns a string with sample characters from \a writingSystem.
2067*/
2068QString QFontDatabase::writingSystemSample(WritingSystem writingSystem)
2069{
2070 return [&]() -> QStringView {
2071 switch (writingSystem) {
2072 case QFontDatabase::Any:
2073 case QFontDatabase::Symbol:
2074 // show only ascii characters
2075 return u"AaBbzZ";
2076 case QFontDatabase::Latin:
2077 // This is cheating... we only show latin-1 characters so that we don't
2078 // end up loading lots of fonts - at least on X11...
2079 return u"Aa\x00C3\x00E1Zz";
2080 case QFontDatabase::Greek:
2081 return u"\x0393\x03B1\x03A9\x03C9";
2082 case QFontDatabase::Cyrillic:
2083 return u"\x0414\x0434\x0436\x044f";
2084 case QFontDatabase::Armenian:
2085 return u"\x053f\x054f\x056f\x057f";
2086 case QFontDatabase::Hebrew:
2087 return u"\x05D0\x05D1\x05D2\x05D3";
2088 case QFontDatabase::Arabic:
2089 return u"\x0623\x0628\x062C\x062F\x064A\x0629\x0020\x0639\x0631\x0628\x064A\x0629";
2090 case QFontDatabase::Syriac:
2091 return u"\x0715\x0725\x0716\x0726";
2092 case QFontDatabase::Thaana:
2093 return u"\x0784\x0794\x078c\x078d";
2094 case QFontDatabase::Devanagari:
2095 return u"\x0905\x0915\x0925\x0935";
2096 case QFontDatabase::Bengali:
2097 return u"\x0986\x0996\x09a6\x09b6";
2098 case QFontDatabase::Gurmukhi:
2099 return u"\x0a05\x0a15\x0a25\x0a35";
2100 case QFontDatabase::Gujarati:
2101 return u"\x0a85\x0a95\x0aa5\x0ab5";
2102 case QFontDatabase::Oriya:
2103 return u"\x0b06\x0b16\x0b2b\x0b36";
2104 case QFontDatabase::Tamil:
2105 return u"\x0b89\x0b99\x0ba9\x0bb9";
2106 case QFontDatabase::Telugu:
2107 return u"\x0c05\x0c15\x0c25\x0c35";
2108 case QFontDatabase::Kannada:
2109 return u"\x0c85\x0c95\x0ca5\x0cb5";
2110 case QFontDatabase::Malayalam:
2111 return u"\x0d05\x0d15\x0d25\x0d35";
2112 case QFontDatabase::Sinhala:
2113 return u"\x0d90\x0da0\x0db0\x0dc0";
2114 case QFontDatabase::Thai:
2115 return u"\x0e02\x0e12\x0e22\x0e32";
2116 case QFontDatabase::Lao:
2117 return u"\x0e8d\x0e9d\x0ead\x0ebd";
2118 case QFontDatabase::Tibetan:
2119 return u"\x0f00\x0f01\x0f02\x0f03";
2120 case QFontDatabase::Myanmar:
2121 return u"\x1000\x1001\x1002\x1003";
2122 case QFontDatabase::Georgian:
2123 return u"\x10a0\x10b0\x10c0\x10d0";
2124 case QFontDatabase::Khmer:
2125 return u"\x1780\x1790\x17b0\x17c0";
2126 case QFontDatabase::SimplifiedChinese:
2127 return u"\x4e2d\x6587\x8303\x4f8b";
2128 case QFontDatabase::TraditionalChinese:
2129 return u"\x4e2d\x6587\x7bc4\x4f8b";
2130 case QFontDatabase::Japanese:
2131 return u"\x30b5\x30f3\x30d7\x30eb\x3067\x3059";
2132 case QFontDatabase::Korean:
2133 return u"\xac00\xac11\xac1a\xac2f";
2134 case QFontDatabase::Vietnamese:
2135 return u"\x1ED7\x1ED9\x1ED1\x1ED3";
2136 case QFontDatabase::Ogham:
2137 return u"\x1681\x1682\x1683\x1684";
2138 case QFontDatabase::Runic:
2139 return u"\x16a0\x16a1\x16a2\x16a3";
2140 case QFontDatabase::Nko:
2141 return u"\x7ca\x7cb\x7cc\x7cd";
2142 default:
2143 return nullptr;
2144 }
2145 }().toString();
2146}
2147
2148void QFontDatabasePrivate::parseFontName(const QString &name, QString &foundry, QString &family)
2149{
2150 QT_PREPEND_NAMESPACE(parseFontName)(name, foundry, family);
2151}
2152
2153// used from qfontengine_ft.cpp
2154Q_GUI_EXPORT QByteArray qt_fontdata_from_index(int index)
2155{
2156 QMutexLocker locker(fontDatabaseMutex());
2157 return QFontDatabasePrivate::instance()->applicationFonts.value(i: index).data;
2158}
2159
2160int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString &fileName)
2161{
2162 QFontDatabasePrivate::ApplicationFont font;
2163 font.data = fontData;
2164 font.fileName = fileName;
2165
2166 Q_TRACE(QFontDatabasePrivate_addAppFont, fileName);
2167
2168 int i;
2169 for (i = 0; i < applicationFonts.size(); ++i)
2170 if (applicationFonts.at(i).isNull())
2171 break;
2172 if (i >= applicationFonts.size()) {
2173 applicationFonts.append(t: ApplicationFont());
2174 i = applicationFonts.size() - 1;
2175 }
2176
2177 if (font.fileName.isEmpty() && !fontData.isEmpty())
2178 font.fileName = ":qmemoryfonts/"_L1 + QString::number(i);
2179
2180 auto *platformFontDatabase = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
2181 platformFontDatabase->addApplicationFont(fontData: font.data, fileName: font.fileName, font: &font);
2182 if (font.properties.isEmpty())
2183 return -1;
2184
2185 applicationFonts[i] = font;
2186
2187 // The font cache may have cached lookups for the font that was now
2188 // loaded, so it has to be flushed.
2189 QFontCache::instance()->clear();
2190
2191 fallbacksCache.clear();
2192
2193 emit qApp->fontDatabaseChanged();
2194
2195 return i;
2196}
2197
2198bool QFontDatabasePrivate::isApplicationFont(const QString &fileName)
2199{
2200 for (int i = 0; i < applicationFonts.size(); ++i)
2201 if (applicationFonts.at(i).fileName == fileName)
2202 return true;
2203 return false;
2204}
2205
2206/*!
2207 \since 4.2
2208
2209 Loads the font from the file specified by \a fileName and makes it available to
2210 the application. An ID is returned that can be used to remove the font again
2211 with removeApplicationFont() or to retrieve the list of family names contained
2212 in the font.
2213
2214//! [add-application-font-doc]
2215 The function returns -1 if the font could not be loaded.
2216
2217 Currently only TrueType fonts, TrueType font collections, and OpenType fonts are
2218 supported.
2219//! [add-application-font-doc]
2220
2221 \sa addApplicationFontFromData(), applicationFontFamilies(), removeApplicationFont()
2222*/
2223int QFontDatabase::addApplicationFont(const QString &fileName)
2224{
2225 QByteArray data;
2226 if (!QFileInfo(fileName).isNativePath()) {
2227 QFile f(fileName);
2228 if (!f.open(flags: QIODevice::ReadOnly))
2229 return -1;
2230
2231 Q_TRACE(QFontDatabase_addApplicationFont, fileName);
2232
2233 data = f.readAll();
2234 }
2235 QMutexLocker locker(fontDatabaseMutex());
2236 return QFontDatabasePrivate::instance()->addAppFont(fontData: data, fileName);
2237}
2238
2239/*!
2240 \since 4.2
2241
2242 Loads the font from binary data specified by \a fontData and makes it available to
2243 the application. An ID is returned that can be used to remove the font again
2244 with removeApplicationFont() or to retrieve the list of family names contained
2245 in the font.
2246
2247 \include qfontdatabase.cpp add-application-font-doc
2248
2249 \sa addApplicationFont(), applicationFontFamilies(), removeApplicationFont()
2250*/
2251int QFontDatabase::addApplicationFontFromData(const QByteArray &fontData)
2252{
2253 QMutexLocker locker(fontDatabaseMutex());
2254 return QFontDatabasePrivate::instance()->addAppFont(fontData, fileName: QString() /* fileName */);
2255}
2256
2257/*!
2258 \since 4.2
2259
2260 Returns a list of font families for the given application font identified by
2261 \a id.
2262
2263 \sa addApplicationFont(), addApplicationFontFromData()
2264*/
2265QStringList QFontDatabase::applicationFontFamilies(int id)
2266{
2267 QMutexLocker locker(fontDatabaseMutex());
2268 auto *d = QFontDatabasePrivate::instance();
2269
2270 QStringList ret;
2271 ret.reserve(asize: d->applicationFonts.value(i: id).properties.size());
2272
2273 for (const auto &properties : d->applicationFonts.value(i: id).properties)
2274 ret.append(t: properties.familyName);
2275
2276 return ret;
2277}
2278
2279/*!
2280 \since 5.2
2281
2282 Returns the most adequate font for a given \a type case for proper integration
2283 with the system's look and feel.
2284
2285 \sa QGuiApplication::font()
2286*/
2287
2288QFont QFontDatabase::systemFont(QFontDatabase::SystemFont type)
2289{
2290 const QFont *font = nullptr;
2291 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
2292 switch (type) {
2293 case GeneralFont:
2294 font = theme->font(type: QPlatformTheme::SystemFont);
2295 break;
2296 case FixedFont:
2297 font = theme->font(type: QPlatformTheme::FixedFont);
2298 break;
2299 case TitleFont:
2300 font = theme->font(type: QPlatformTheme::TitleBarFont);
2301 break;
2302 case SmallestReadableFont:
2303 font = theme->font(type: QPlatformTheme::MiniFont);
2304 break;
2305 }
2306 }
2307
2308 if (font)
2309 return *font;
2310 else if (QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration())
2311 return integration->fontDatabase()->defaultFont();
2312 else
2313 return QFont();
2314}
2315
2316/*!
2317 \fn bool QFontDatabase::removeApplicationFont(int id)
2318 \since 4.2
2319
2320 Removes the previously loaded application font identified by \a
2321 id. Returns \c true if unloading of the font succeeded; otherwise
2322 returns \c false.
2323
2324 \sa removeAllApplicationFonts(), addApplicationFont(),
2325 addApplicationFontFromData()
2326*/
2327bool QFontDatabase::removeApplicationFont(int handle)
2328{
2329 QMutexLocker locker(fontDatabaseMutex());
2330
2331 auto *db = QFontDatabasePrivate::instance();
2332 if (handle < 0 || handle >= db->applicationFonts.size())
2333 return false;
2334
2335 db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
2336
2337 db->invalidate();
2338 return true;
2339}
2340
2341/*!
2342 \fn bool QFontDatabase::removeAllApplicationFonts()
2343 \since 4.2
2344
2345 Removes all application-local fonts previously added using addApplicationFont()
2346 and addApplicationFontFromData().
2347
2348 Returns \c true if unloading of the fonts succeeded; otherwise
2349 returns \c false.
2350
2351 \sa removeApplicationFont(), addApplicationFont(), addApplicationFontFromData()
2352*/
2353bool QFontDatabase::removeAllApplicationFonts()
2354{
2355 QMutexLocker locker(fontDatabaseMutex());
2356
2357 auto *db = QFontDatabasePrivate::instance();
2358 if (!db || db->applicationFonts.isEmpty())
2359 return false;
2360
2361 db->applicationFonts.clear();
2362 db->invalidate();
2363 return true;
2364}
2365
2366/*!
2367 \since 6.8
2368
2369 Adds \a familyName as an application-defined fallback font for \a script.
2370
2371 When Qt encounters characters that are not supported by the selected font, it will search
2372 through a list of fallback fonts to find a match for them. This ensures that combining multiple
2373 scripts in a single string is possible, even if the main font does not support them.
2374
2375 The list of fallback fonts is selected based on the script of the string as well as other
2376 conditions, such as system language.
2377
2378 While the system fallback list is usually sufficient, there are cases where it is useful
2379 to override the default behavior. One such case is for using application fonts as fallback to
2380 ensure cross-platform consistency.
2381
2382 In another case the application may be written in a script with regional differences and want
2383 to run it untranslated in multiple regions. In this case, it might be useful to override the
2384 local region's fallback with one that matches the language of the application.
2385
2386 By passing \a familyName to addApplicationFallbackFontFamily(), this will become the preferred
2387 family when matching missing characters from \a script. The \a script must be a valid script
2388 (\c QChar::Script_Latin or higher). When adding multiple fonts for the same script, they will
2389 be prioritized in reverse order, so that the last family added will be checked first and so
2390 on.
2391
2392 \note Qt's font matching algorithm considers \c{QChar::Script_Common} (undetermined script)
2393 and \c{QChar::Script_Latin} the same. Adding a fallback for either of these will also apply
2394 to the other.
2395
2396 \sa setApplicationFallbackFontFamilies(), removeApplicationFallbackFontFamily(), applicationFallbackFontFamilies()
2397*/
2398void QFontDatabase::addApplicationFallbackFontFamily(QChar::Script script, const QString &familyName)
2399{
2400 QMutexLocker locker(fontDatabaseMutex());
2401
2402 if (script < QChar::Script_Common) {
2403 qCWarning(lcFontDb) << "Invalid script passed to addApplicationFallbackFontFamily:" << script;
2404 return;
2405 }
2406
2407 if (script == QChar::Script_Latin)
2408 script = QChar::Script_Common;
2409
2410 auto *db = QFontDatabasePrivate::instance();
2411 auto it = db->applicationFallbackFontFamilies.find(key: script);
2412 if (it == db->applicationFallbackFontFamilies.end())
2413 it = db->applicationFallbackFontFamilies.insert(key: script, value: QStringList{});
2414
2415 it->prepend(t: familyName);
2416
2417 QFontCache::instance()->clear();
2418 db->fallbacksCache.clear();
2419}
2420
2421/*!
2422 \since 6.8
2423
2424 Removes \a familyName from the list of application-defined fallback fonts for \a script,
2425 provided that it has previously been added with \l{addApplicationFallbackFontFamily()}.
2426
2427 Returns true if the family name was in the list and false if it was not.
2428
2429 \sa addApplicationFallbackFontFamily(), setApplicationFallbackFontFamilies(), applicationFallbackFontFamilies()
2430*/
2431bool QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script script, const QString &familyName)
2432{
2433 QMutexLocker locker(fontDatabaseMutex());
2434
2435 if (script < QChar::Script_Common) {
2436 qCWarning(lcFontDb) << "Invalid script passed to removeApplicationFallbackFontFamily:" << script;
2437 return false;
2438 }
2439
2440 if (script == QChar::Script_Latin)
2441 script = QChar::Script_Common;
2442
2443 auto *db = QFontDatabasePrivate::instance();
2444 auto it = db->applicationFallbackFontFamilies.find(key: script);
2445 if (it != db->applicationFallbackFontFamilies.end()) {
2446 if (it->removeAll(t: familyName) > 0) {
2447 if (it->isEmpty())
2448 it = db->applicationFallbackFontFamilies.erase(it);
2449 QFontCache::instance()->clear();
2450 db->fallbacksCache.clear();
2451 return true;
2452 }
2453 }
2454
2455 return false;
2456}
2457
2458/*!
2459 \since 6.8
2460
2461 Sets the list of application-defined fallback fonts for \a script to \a familyNames.
2462
2463 When Qt encounters a character in \a script which is not supported by the current font, it will
2464 check the families in \a familyNames, in order from first to last, until it finds a match. See
2465 \l{addApplicationFallbackFontFamily()} for more details.
2466
2467 This function overwrites the current list of application-defined fallback fonts for \a script.
2468
2469 \sa addApplicationFallbackFontFamily(), removeApplicationFallbackFontFamily(), applicationFallbackFontFamilies()
2470*/
2471void QFontDatabase::setApplicationFallbackFontFamilies(QChar::Script script, const QStringList &familyNames)
2472{
2473 QMutexLocker locker(fontDatabaseMutex());
2474
2475 if (script < QChar::Script_Common) {
2476 qCWarning(lcFontDb) << "Invalid script passed to setApplicationFallbackFontFamilies:" << script;
2477 return;
2478 }
2479
2480 if (script == QChar::Script_Latin)
2481 script = QChar::Script_Common;
2482
2483 auto *db = QFontDatabasePrivate::instance();
2484 db->applicationFallbackFontFamilies[script] = familyNames;
2485
2486 QFontCache::instance()->clear();
2487 db->fallbacksCache.clear();
2488}
2489
2490/*!
2491 \since 6.8
2492
2493 Returns the list of application-defined fallback font families previously added for \a script
2494 by the \l{addApplicationFallbackFontFamily()} function.
2495
2496 \sa setApplicationFallbackFontFamilies(), addApplicationFallbackFontFamily(), removeApplicationFallbackFontFamily()
2497*/
2498QStringList QFontDatabase::applicationFallbackFontFamilies(QChar::Script script)
2499{
2500 QMutexLocker locker(fontDatabaseMutex());
2501
2502 if (script == QChar::Script_Latin)
2503 script = QChar::Script_Common;
2504
2505 auto *db = QFontDatabasePrivate::instance();
2506 return db->applicationFallbackFontFamilies.value(key: script);
2507}
2508
2509/*!
2510 \internal
2511*/
2512QFontEngine *QFontDatabasePrivate::findFont(const QFontDef &req,
2513 int script,
2514 bool preferScriptOverFamily)
2515{
2516 QMutexLocker locker(fontDatabaseMutex());
2517 ensureFontDatabase();
2518
2519 QFontEngine *engine;
2520
2521#ifdef Q_OS_WIN
2522 const QFontDef request = static_cast<QWindowsFontDatabaseBase *>(
2523 QGuiApplicationPrivate::platformIntegration()->fontDatabase())
2524 ->sanitizeRequest(req);
2525#else
2526 const QFontDef &request = req;
2527#endif
2528
2529#if defined(QT_BUILD_INTERNAL)
2530 // For testing purpose only, emulates an exact-matching monospace font
2531 if (qt_enable_test_font && request.families.first() == "__Qt__Box__Engine__"_L1) {
2532 engine = new QTestFontEngine(request.pixelSize);
2533 engine->fontDef = request;
2534 return engine;
2535 }
2536#endif
2537
2538 QFontCache *fontCache = QFontCache::instance();
2539
2540 // Until we specifically asked not to, try looking for Multi font engine
2541 // first, the last '1' indicates that we want Multi font engine instead
2542 // of single ones
2543 bool multi = !(request.styleStrategy & QFont::NoFontMerging);
2544 QFontCache::Key key(request, script, multi ? 1 : 0);
2545 engine = fontCache->findEngine(key);
2546 if (engine) {
2547 qCDebug(lcFontMatch, "Cache hit level 1");
2548 return engine;
2549 }
2550
2551 if (request.pixelSize > 0xffff) {
2552 // Stop absurd requests reaching the engines; pixel size is assumed to fit ushort
2553 qCDebug(lcFontMatch, "Rejecting request for pixel size %g2, returning box engine", double(request.pixelSize));
2554 return new QFontEngineBox(32); // not request.pixelSize, to avoid overflow/DOS
2555 }
2556
2557 QString family_name, foundry_name;
2558 const QString requestFamily = request.families.at(i: 0);
2559 parseFontName(name: requestFamily, foundry&: foundry_name, family&: family_name);
2560 QtFontDesc desc;
2561 QList<int> blackListed;
2562 unsigned int score = UINT_MAX;
2563 int index = match(script: multi ? QChar::Script_Common : script, request, family_name, foundry_name, desc: &desc, blacklistedFamilies: blackListed, resultingScore: &score);
2564 if (score > 0 && QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamilyAliases(missingFamily: family_name)) {
2565 // We populated family aliases (e.g. localized families), so try again
2566 index = match(script: multi ? QChar::Script_Common : script, request, family_name, foundry_name, desc: &desc, blacklistedFamilies: blackListed);
2567 }
2568
2569 // If we do not find a match and NoFontMerging is set, use the requested font even if it does
2570 // not support the script.
2571 //
2572 // (we do this at the end to prefer foundries that support the script if they exist)
2573 if (index < 0 && !multi && !preferScriptOverFamily)
2574 index = match(script: QChar::Script_Common, request, family_name, foundry_name, desc: &desc, blacklistedFamilies: blackListed);
2575
2576 if (index >= 0) {
2577 QFontDef fontDef = request;
2578 // Don't pass empty family names to the platform font database, since it will then invoke its own matching
2579 // and we will be out of sync with the matched font.
2580 if (fontDef.families.isEmpty())
2581 fontDef.families = QStringList(desc.family->name);
2582
2583 engine = loadEngine(script, request: fontDef, family: desc.family, foundry: desc.foundry, style: desc.style, size: desc.size);
2584
2585 if (engine)
2586 initFontDef(desc, request, fontDef: &engine->fontDef, multi);
2587 else
2588 blackListed.append(t: index);
2589 } else {
2590 qCDebug(lcFontMatch, " NO MATCH FOUND\n");
2591 }
2592
2593 if (!engine) {
2594 if (!requestFamily.isEmpty()) {
2595 QFont::StyleHint styleHint = QFont::StyleHint(request.styleHint);
2596 if (styleHint == QFont::AnyStyle && request.fixedPitch)
2597 styleHint = QFont::TypeWriter;
2598
2599 QStringList fallbacks = request.fallBackFamilies
2600 + fallbacksForFamily(family: requestFamily,
2601 style: QFont::Style(request.style),
2602 styleHint,
2603 script: QChar::Script(script));
2604 if (script > QChar::Script_Common)
2605 fallbacks += QString(); // Find the first font matching the specified script.
2606
2607 for (int i = 0; !engine && i < fallbacks.size(); i++) {
2608 QFontDef def = request;
2609 def.families = QStringList(fallbacks.at(i));
2610 QFontCache::Key key(def, script, multi ? 1 : 0);
2611 engine = fontCache->findEngine(key);
2612 if (!engine) {
2613 QtFontDesc desc;
2614 do {
2615 index = match(script: multi ? QChar::Script_Common : script, request: def, family_name: def.families.constFirst(), foundry_name: ""_L1, desc: &desc, blacklistedFamilies: blackListed);
2616 if (index >= 0) {
2617 QFontDef loadDef = def;
2618 if (loadDef.families.isEmpty())
2619 loadDef.families = QStringList(desc.family->name);
2620 engine = loadEngine(script, request: loadDef, family: desc.family, foundry: desc.foundry, style: desc.style, size: desc.size);
2621 if (engine)
2622 initFontDef(desc, request: loadDef, fontDef: &engine->fontDef, multi);
2623 else
2624 blackListed.append(t: index);
2625 }
2626 } while (index >= 0 && !engine);
2627 }
2628 }
2629 }
2630
2631 if (!engine)
2632 engine = new QFontEngineBox(request.pixelSize);
2633
2634 qCDebug(lcFontMatch, "returning box engine");
2635 }
2636
2637 return engine;
2638}
2639
2640void QFontDatabasePrivate::load(const QFontPrivate *d, int script)
2641{
2642 QFontDef req = d->request;
2643
2644 if (req.pixelSize == -1) {
2645 req.pixelSize = std::floor(x: ((req.pointSize * d->dpi) / 72) * 100 + 0.5) / 100;
2646 req.pixelSize = qRound(d: req.pixelSize);
2647 }
2648
2649 if (req.pointSize < 0 && d->dpi > 0)
2650 req.pointSize = req.pixelSize*72.0/d->dpi;
2651
2652 // respect the fallback families that might be passed through the request
2653 const QStringList fallBackFamilies = familyList(req);
2654
2655 if (!d->engineData) {
2656 QFontCache *fontCache = QFontCache::instance();
2657 // look for the requested font in the engine data cache
2658 // note: fallBackFamilies are not respected in the EngineData cache key;
2659 // join them with the primary selection family to avoid cache misses
2660 if (!d->request.families.isEmpty())
2661 req.families = fallBackFamilies;
2662
2663 d->engineData = fontCache->findEngineData(def: req);
2664 if (!d->engineData) {
2665 // create a new one
2666 d->engineData = new QFontEngineData;
2667 fontCache->insertEngineData(def: req, engineData: d->engineData);
2668 }
2669 d->engineData->ref.ref();
2670 }
2671
2672 // the cached engineData could have already loaded the engine we want
2673 if (d->engineData->engines[script])
2674 return;
2675
2676 QFontEngine *fe = nullptr;
2677
2678 Q_TRACE(QFontDatabase_load, req.families.join(QLatin1Char(';')), req.pointSize);
2679
2680 req.fallBackFamilies = fallBackFamilies;
2681 if (!req.fallBackFamilies.isEmpty())
2682 req.families = QStringList(req.fallBackFamilies.takeFirst());
2683
2684 // list of families to try
2685 QStringList family_list;
2686
2687 if (!req.families.isEmpty()) {
2688 // Add primary selection
2689 family_list << req.families.at(i: 0);
2690
2691 // add the default family
2692 const auto families = QGuiApplication::font().families();
2693 if (!families.isEmpty()) {
2694 QString defaultFamily = families.first();
2695 if (! family_list.contains(str: defaultFamily))
2696 family_list << defaultFamily;
2697 }
2698
2699 }
2700
2701 // null family means find the first font matching the specified script
2702 family_list << QString();
2703
2704 QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd();
2705 for (; !fe && it != end; ++it) {
2706 req.families = QStringList(*it);
2707
2708 fe = QFontDatabasePrivate::findFont(req, script);
2709 if (fe) {
2710 if (fe->type() == QFontEngine::Box && !req.families.at(i: 0).isEmpty()) {
2711 if (fe->ref.loadRelaxed() == 0)
2712 delete fe;
2713 fe = nullptr;
2714 } else {
2715 if (d->dpi > 0)
2716 fe->fontDef.pointSize = qreal(double((fe->fontDef.pixelSize * 72) / d->dpi));
2717 }
2718 }
2719
2720 // No need to check requested fallback families again
2721 req.fallBackFamilies.clear();
2722 }
2723
2724 Q_ASSERT(fe);
2725 if (fe->symbol || (d->request.styleStrategy & QFont::NoFontMerging)) {
2726 for (int i = 0; i < QChar::ScriptCount; ++i) {
2727 if (!d->engineData->engines[i]) {
2728 d->engineData->engines[i] = fe;
2729 fe->ref.ref();
2730 }
2731 }
2732 } else {
2733 d->engineData->engines[script] = fe;
2734 fe->ref.ref();
2735 }
2736}
2737
2738QString QFontDatabasePrivate::resolveFontFamilyAlias(const QString &family)
2739{
2740 return QGuiApplicationPrivate::platformIntegration()->fontDatabase()->resolveFontFamilyAlias(family);
2741}
2742
2743Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script, const QStringList &families)
2744{
2745 size_t writingSystem = qt_writing_system_for_script(script);
2746 if (writingSystem == QFontDatabase::Any
2747 || writingSystem >= QFontDatabase::WritingSystemsCount) {
2748 return families;
2749 }
2750
2751 auto *db = QFontDatabasePrivate::instance();
2752 QMultiMap<uint, QString> supported;
2753 for (int i = 0; i < families.size(); ++i) {
2754 const QString &family = families.at(i);
2755
2756 QtFontFamily *testFamily = nullptr;
2757 for (int x = 0; x < db->count; ++x) {
2758 if (Q_UNLIKELY(matchFamilyName(family, db->families[x]))) {
2759 testFamily = db->families[x];
2760 if (testFamily->ensurePopulated())
2761 break;
2762 }
2763 }
2764
2765 uint order = i;
2766 if (testFamily == nullptr
2767 || !familySupportsWritingSystem(family: testFamily, writingSystem)) {
2768 order |= 1u << 31;
2769 }
2770
2771 supported.insert(key: order, value: family);
2772 }
2773
2774 return supported.values();
2775}
2776
2777QT_END_NAMESPACE
2778
2779#include "moc_qfontdatabase.cpp"
2780
2781

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