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

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