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