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