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