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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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