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