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