21#include <private/qfontengine_p.h>
22#include <private/qpainter_p.h>
23#include <private/qtextengine_p.h>
26#include <qpa/qplatformscreen.h>
27#include <qpa/qplatformintegration.h>
28#include <qpa/qplatformfontdatabase.h>
29#include <QtGui/private/qguiapplication_p.h>
31#include <QtCore/QMutexLocker>
32#include <QtCore/QMutex>
38#ifdef QFONTCACHE_DEBUG
39# define FC_DEBUG qDebug
41# define FC_DEBUG if (false) qDebug
46#ifndef QFONTCACHE_DECREASE_TRIGGER_LIMIT
47# define QFONTCACHE_DECREASE_TRIGGER_LIMIT 256
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 if (pixelSize != -1 && other.pixelSize != -1) {
71 if (pixelSize != other.pixelSize)
73 }
else if (pointSize != -1 && other.pointSize != -1) {
74 if (pointSize != other.pointSize)
86 if (families.size() != other.families.size())
89 QString this_family, this_foundry, other_family, other_foundry;
90 for (
int i = 0; i < families.size(); ++i) {
91 QFontDatabasePrivate::parseFontName(families.at(i), this_foundry, this_family);
92 QFontDatabasePrivate::parseFontName(other.families.at(i), other_foundry, other_family);
93 if (this_family != other_family || this_foundry != other_foundry)
97 if (variableAxisValues != other.variableAxisValues)
100 return (styleHint == other.styleHint
101 && styleStrategy == other.styleStrategy
102 && weight == other.weight
103 && style == other.style
104 && this_family == other_family
105 && (styleName.isEmpty() || other.styleName.isEmpty() || styleName == other.styleName)
106 && (this_foundry.isEmpty()
107 || other_foundry.isEmpty()
108 || this_foundry == other_foundry)
114Q_GUI_EXPORT QPoint qt_defaultDpis()
116 if (QCoreApplication::instance()->testAttribute(Qt::AA_Use96Dpi))
117 return QPoint(96, 96);
120 return QPoint(75, 75);
122 int dpis = QGuiApplicationPrivate::m_primaryScreenDpis.loadRelaxed();
123 int dpiX = (dpis >> 16) & 0xffff;
124 int dpiY = dpis & 0xffff;
125 if (dpiX > 0 && dpiY > 0)
126 return QPoint(dpiX, dpiY);
129 return QPoint(100, 100);
134 return qt_defaultDpis().x();
139 return qt_defaultDpis().y();
144 return qt_defaultDpiY();
150 static constexpr std::array<
int, 2> legacyToOpenTypeMap[] = {
151 { 0, QFont::Thin }, { 12, QFont::ExtraLight }, { 25, QFont::Light },
152 { 50, QFont::Normal }, { 57, QFont::Medium }, { 63, QFont::DemiBold },
153 { 75, QFont::Bold }, { 81, QFont::ExtraBold }, { 87, QFont::Black },
156 int closestDist = INT_MAX;
160 for (
auto mapping : legacyToOpenTypeMap) {
161 const int weightOld = mapping[ inverted];
162 const int weightNew = mapping[!inverted];
163 const int dist = qAbs(weightOld - weight);
164 if (dist < closestDist) {
179 QStringList familyList;
180 if (family.isEmpty())
182 const auto list = QStringView{family}.split(u',');
183 const int numFamilies = list.size();
184 familyList.reserve(numFamilies);
185 for (
int i = 0; i < numFamilies; ++i) {
186 auto str = list.at(i).trimmed();
187 if ((str.startsWith(u'"') && str.endsWith(u'"'))
188 || (str.startsWith(u'\'') && str.endsWith(u'\''))) {
189 str = str.mid(1, str.size() - 2);
191 familyList << str.toString();
208QFontPrivate::QFontPrivate()
209 : engineData(
nullptr), dpi(qt_defaultDpi()),
210 underline(
false), overline(
false), strikeOut(
false), kerning(
true),
211 capital(0), letterSpacingIsAbsolute(
false), scFont(
nullptr)
215QFontPrivate::QFontPrivate(
const QFontPrivate &other)
216 : request(other.request), engineData(
nullptr), dpi(other.dpi),
217 underline(other.underline), overline(other.overline),
218 strikeOut(other.strikeOut), kerning(other.kerning),
219 capital(other.capital), letterSpacingIsAbsolute(other.letterSpacingIsAbsolute),
220 letterSpacing(other.letterSpacing), wordSpacing(other.wordSpacing),
221 features(other.features), scFont(other.scFont)
223 if (scFont && scFont !=
this)
227QFontPrivate::~QFontPrivate()
229 if (engineData && !engineData->ref.deref())
231 engineData =
nullptr;
232 if (scFont && scFont !=
this) {
233 if (!scFont->ref.deref())
241#define QT_FONT_ENGINE_FROM_DATA(data, script) data->engines[script]
243QFontEngine *QFontPrivate::engineForScript(
int script)
const
245 QMutexLocker locker(qt_fontdatabase_mutex());
246 if (script <= QChar::Script_Latin)
247 script = QChar::Script_Common;
248 if (engineData && engineData->fontCacheId != QFontCache::instance()->id()) {
250 if (!engineData->ref.deref())
252 engineData =
nullptr;
255 QFontDatabasePrivate::load(
this, script);
259QFontEngine *QFontPrivate::engineForCharacter(
char32_t c, EngineQueryOptions opt)
const
261 const bool smallCaps = !(opt & EngineQueryOption::IgnoreSmallCapsEngine);
262 const auto script = QChar::script(c);
264 if (smallCaps && capital == QFont::SmallCaps && QChar::isLower(c))
265 engine = smallCapsFontPrivate()->engineForScript(script);
267 engine = engineForScript(script);
268 Q_ASSERT(engine !=
nullptr);
272void QFontPrivate::alterCharForCapitalization(QChar &c)
const {
274 case QFont::AllUppercase:
275 case QFont::SmallCaps:
278 case QFont::AllLowercase:
281 case QFont::MixedCase:
286QFontPrivate *QFontPrivate::smallCapsFontPrivate()
const
290 QFont font(
const_cast<QFontPrivate *>(
this));
291 qreal pointSize = font.pointSizeF();
293 font.setPointSizeF(pointSize * .7);
295 font.setPixelSize((font.pixelSize() * 7 + 5) / 10);
296 scFont = font.d.data();
303void QFontPrivate::resolve(uint mask,
const QFontPrivate *other)
305 Q_ASSERT(other !=
nullptr);
309 if ((mask & QFont::AllPropertiesResolved) == QFont::AllPropertiesResolved)
return;
312 if (!(mask & QFont::FamiliesResolved))
313 request.families = other->request.families;
315 if (! (mask & QFont::StyleNameResolved))
316 request.styleName = other->request.styleName;
318 if (! (mask & QFont::SizeResolved)) {
319 request.pointSize = other->request.pointSize;
320 request.pixelSize = other->request.pixelSize;
323 if (! (mask & QFont::StyleHintResolved))
324 request.styleHint = other->request.styleHint;
326 if (! (mask & QFont::StyleStrategyResolved))
327 request.styleStrategy = other->request.styleStrategy;
329 if (! (mask & QFont::WeightResolved))
330 request.weight = other->request.weight;
332 if (! (mask & QFont::StyleResolved))
333 request.style = other->request.style;
335 if (! (mask & QFont::FixedPitchResolved))
336 request.fixedPitch = other->request.fixedPitch;
338 if (! (mask & QFont::StretchResolved))
339 request.stretch = other->request.stretch;
341 if (! (mask & QFont::HintingPreferenceResolved))
342 request.hintingPreference = other->request.hintingPreference;
344 if (! (mask & QFont::UnderlineResolved))
345 underline = other->underline;
347 if (! (mask & QFont::OverlineResolved))
348 overline = other->overline;
350 if (! (mask & QFont::StrikeOutResolved))
351 strikeOut = other->strikeOut;
353 if (! (mask & QFont::KerningResolved))
354 kerning = other->kerning;
356 if (! (mask & QFont::LetterSpacingResolved)) {
357 letterSpacing = other->letterSpacing;
358 letterSpacingIsAbsolute = other->letterSpacingIsAbsolute;
360 if (! (mask & QFont::WordSpacingResolved))
361 wordSpacing = other->wordSpacing;
362 if (! (mask & QFont::CapitalizationResolved))
363 capital = other->capital;
365 if (!(mask & QFont::FeaturesResolved))
366 features = other->features;
368 if (!(mask & QFont::VariableAxesResolved))
369 request.variableAxisValues = other->request.variableAxisValues;
372bool QFontPrivate::hasVariableAxis(QFont::Tag tag,
float value)
const
374 return request.variableAxisValues.contains(tag) && request.variableAxisValues.value(tag) == value;
377void QFontPrivate::setVariableAxis(QFont::Tag tag,
float value)
379 request.variableAxisValues.insert(tag, value);
382void QFontPrivate::unsetVariableAxis(QFont::Tag tag)
384 request.variableAxisValues.remove(tag);
387void QFontPrivate::setFeature(QFont::Tag tag, quint32 value)
389 features.insert(tag, value);
392void QFontPrivate::unsetFeature(QFont::Tag tag)
394 features.remove(tag);
401 memset(engines, 0, QFontDatabasePrivate::ScriptCount *
sizeof(QFontEngine *));
406 Q_ASSERT(ref.loadRelaxed() == 0);
407 for (
int i = 0; i < QFontDatabasePrivate::ScriptCount; ++i) {
409 if (!engines[i]->ref.deref())
411 engines[i] =
nullptr;
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
609
610
611
612
613
614
615
616
617
618
619
620
621
622
625
626
627
628
629
630
633
634
635
636QFont::QFont(
const QFont &font,
const QPaintDevice *pd)
637 : resolve_mask(font.resolve_mask)
640 const int dpi = pd->logicalDpiY();
641 if (font.d->dpi != dpi) {
642 d =
new QFontPrivate(*font.d);
650
651
652QFont::QFont(QFontPrivate *data)
653 : d(data), resolve_mask(QFont::AllPropertiesResolved)
658
659
662 if (d->ref.loadRelaxed() == 1) {
663 if (d->engineData && !d->engineData->ref.deref())
664 delete d->engineData;
665 d->engineData =
nullptr;
666 if (d->scFont && d->scFont != d.data()) {
667 if (!d->scFont->ref.deref())
678
679
680
681
682
683void QFontPrivate::detachButKeepEngineData(QFont *font)
685 if (font->d->ref.loadRelaxed() == 1)
688 QFontEngineData *engineData = font->d->engineData;
690 engineData->ref.ref();
692 font->d->engineData = engineData;
696
697
698
699
701 : d(QGuiApplicationPrivate::instance() ? QGuiApplication::font().d.data() :
new QFontPrivate()), resolve_mask(0)
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727QFont::QFont(
const QString &family,
int pointSize,
int weight,
bool italic)
728 : d(
new QFontPrivate()), resolve_mask(QFont::FamiliesResolved)
730 if (pointSize <= 0) {
733 resolve_mask |= QFont::SizeResolved;
739 resolve_mask |= QFont::WeightResolved | QFont::StyleResolved;
743 resolve_mask |= QFont::StyleResolved;
745 d->request.families = splitIntoFamilies(family);
746 d->request.pointSize = qreal(pointSize);
747 d->request.pixelSize = -1;
748 d->request.weight = weight;
749 d->request.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770QFont::QFont(
const QStringList &families,
int pointSize,
int weight,
bool italic)
771 : d(
new QFontPrivate()), resolve_mask(QFont::FamiliesResolved)
776 resolve_mask |= QFont::SizeResolved;
781 resolve_mask |= QFont::WeightResolved | QFont::StyleResolved;
784 resolve_mask |= QFont::StyleResolved;
786 d->request.families = families;
787 d->request.pointSize = qreal(pointSize);
788 d->request.pixelSize = -1;
789 d->request.weight = weight;
790 d->request.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
794
795
796QFont::QFont(
const QFont &font)
797 : d(font.d), resolve_mask(font.resolve_mask)
802
803
809
810
811QFont &QFont::operator=(
const QFont &font)
814 resolve_mask = font.resolve_mask;
819
820
821
822
825
826
827
828
829
830QString QFont::family()
const
832 return d->request.families.isEmpty() ? QString() : d->request.families.constFirst();
836
837
838
839
840
841
842
843
844
845
846
847
848void QFont::setFamily(
const QString &family)
850 setFamilies(QStringList(family));
854
855
856
857
858
859
860
861
862QString QFont::styleName()
const
864 return d->request.styleName;
868
869
870
871
872
873
874
875
876
877
878
879
880void QFont::setStyleName(
const QString &styleName)
882 if ((resolve_mask & QFont::StyleNameResolved) && d->request.styleName == styleName)
887 d->request.styleName = styleName;
888 resolve_mask |= QFont::StyleNameResolved;
892
893
894
895
896
897int QFont::pointSize()
const
899 return qRound(d->request.pointSize);
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
961
962
963
964
965
966
967
968
969
970void QFont::setHintingPreference(HintingPreference hintingPreference)
972 if ((resolve_mask & QFont::HintingPreferenceResolved) && d->request.hintingPreference == hintingPreference)
977 d->request.hintingPreference = hintingPreference;
979 resolve_mask |= QFont::HintingPreferenceResolved;
983
984
985
986
987QFont::HintingPreference QFont::hintingPreference()
const
989 return QFont::HintingPreference(d->request.hintingPreference);
993
994
995
996
997
998void QFont::setPointSize(
int pointSize)
1000 if (pointSize <= 0) {
1001 qWarning(
"QFont::setPointSize: Point size <= 0 (%d), must be greater than 0", pointSize);
1005 if ((resolve_mask & QFont::SizeResolved) && d->request.pointSize == qreal(pointSize))
1010 d->request.pointSize = qreal(pointSize);
1011 d->request.pixelSize = -1;
1013 resolve_mask |= QFont::SizeResolved;
1017
1018
1019
1020
1021
1022
1023void QFont::setPointSizeF(qreal pointSize)
1025 if (pointSize <= 0) {
1026 qWarning(
"QFont::setPointSizeF: Point size <= 0 (%f), must be greater than 0", pointSize);
1030 if ((resolve_mask & QFont::SizeResolved) && d->request.pointSize == pointSize)
1035 d->request.pointSize = pointSize;
1036 d->request.pixelSize = -1;
1038 resolve_mask |= QFont::SizeResolved;
1042
1043
1044
1045
1046
1047qreal QFont::pointSizeF()
const
1049 return d->request.pointSize;
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062void QFont::setPixelSize(
int pixelSize)
1064 if (pixelSize <= 0) {
1065 qWarning(
"QFont::setPixelSize: Pixel size <= 0 (%d)", pixelSize);
1069 if ((resolve_mask & QFont::SizeResolved) && d->request.pixelSize == qreal(pixelSize))
1074 d->request.pixelSize = pixelSize;
1075 d->request.pointSize = -1;
1077 resolve_mask |= QFont::SizeResolved;
1081
1082
1083
1084
1085
1086
1087int QFont::pixelSize()
const
1089 return d->request.pixelSize;
1093
1094
1095
1096
1097
1098
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1114
1115
1116
1117
1118QFont::Style QFont::style()
const
1120 return (QFont::Style)d->request.style;
1125
1126
1127
1128
1129void QFont::setStyle(Style style)
1131 if ((resolve_mask & QFont::StyleResolved) && d->request.style == style)
1136 d->request.style = style;
1137 resolve_mask |= QFont::StyleResolved;
1141
1142
1143
1144
1145
1146QFont::Weight QFont::weight()
const
1148 return static_cast<Weight>(d->request.weight);
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1170#if QT_DEPRECATED_SINCE(6
, 0
)
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186void QFont::setLegacyWeight(
int legacyWeight)
1188 setWeight(QFont::Weight(qt_legacyToOpenTypeWeight(legacyWeight)));
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204int QFont::legacyWeight()
const
1206 return qt_openTypeToLegacyWeight(weight());
1211
1212
1213
1214
1215
1216
1217
1218void QFont::setWeight(QFont::Weight weight)
1221 if (weightValue !=
static_cast<
int>(weight)) {
1222 qWarning() <<
"QFont::setWeight: Weight must be between 1 and 1000, attempted to set "
1223 <<
static_cast<
int>(weight);
1226 if ((resolve_mask & QFont::WeightResolved) && d->request.weight == weightValue)
1231 d->request.weight = weightValue;
1232 resolve_mask |= QFont::WeightResolved;
1236
1237
1238
1239
1240
1241
1242
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1260
1261
1262
1263
1264bool QFont::underline()
const
1266 return d->underline;
1270
1271
1272
1273
1274
1275void QFont::setUnderline(
bool enable)
1277 if ((resolve_mask & QFont::UnderlineResolved) && d->underline == enable)
1280 QFontPrivate::detachButKeepEngineData(
this);
1282 d->underline = enable;
1283 resolve_mask |= QFont::UnderlineResolved;
1287
1288
1289
1290
1291bool QFont::overline()
const
1297
1298
1299
1300
1301void QFont::setOverline(
bool enable)
1303 if ((resolve_mask & QFont::OverlineResolved) && d->overline == enable)
1306 QFontPrivate::detachButKeepEngineData(
this);
1308 d->overline = enable;
1309 resolve_mask |= QFont::OverlineResolved;
1313
1314
1315
1316
1317bool QFont::strikeOut()
const
1319 return d->strikeOut;
1323
1324
1325
1326
1327
1328void QFont::setStrikeOut(
bool enable)
1330 if ((resolve_mask & QFont::StrikeOutResolved) && d->strikeOut == enable)
1333 QFontPrivate::detachButKeepEngineData(
this);
1335 d->strikeOut = enable;
1336 resolve_mask |= QFont::StrikeOutResolved;
1340
1341
1342
1343
1344bool QFont::fixedPitch()
const
1346 return d->request.fixedPitch;
1350
1351
1352
1353
1354
1355void QFont::setFixedPitch(
bool enable)
1357 if ((resolve_mask & QFont::FixedPitchResolved) && d->request.fixedPitch == enable)
1362 d->request.fixedPitch = enable;
1363 d->request.ignorePitch =
false;
1364 resolve_mask |= QFont::FixedPitchResolved;
1368
1369
1370
1371
1372bool QFont::kerning()
const
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388void QFont::setKerning(
bool enable)
1390 if ((resolve_mask & QFont::KerningResolved) && d->kerning == enable)
1393 QFontPrivate::detachButKeepEngineData(
this);
1395 d->kerning = enable;
1396 resolve_mask |= QFont::KerningResolved;
1400
1401
1402
1403
1404
1405
1406
1407QFont::StyleStrategy QFont::styleStrategy()
const
1409 return (StyleStrategy) d->request.styleStrategy;
1413
1414
1415
1416
1417
1418
1419
1420QFont::StyleHint QFont::styleHint()
const
1422 return (StyleHint) d->request.styleHint;
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535void QFont::setStyleHint(StyleHint hint, StyleStrategy strategy)
1537 if ((resolve_mask & (QFont::StyleHintResolved | QFont::StyleStrategyResolved)) &&
1538 (StyleHint) d->request.styleHint == hint &&
1539 (StyleStrategy) d->request.styleStrategy == strategy)
1544 d->request.styleHint = hint;
1545 d->request.styleStrategy = strategy;
1546 resolve_mask |= QFont::StyleHintResolved;
1547 resolve_mask |= QFont::StyleStrategyResolved;
1552
1553
1554
1555
1556void QFont::setStyleStrategy(StyleStrategy s)
1558 if ((resolve_mask & QFont::StyleStrategyResolved) &&
1559 s == (StyleStrategy)d->request.styleStrategy)
1564 d->request.styleStrategy = s;
1565 resolve_mask |= QFont::StyleStrategyResolved;
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1590
1591
1592
1593
1594int QFont::stretch()
const
1596 return d->request.stretch;
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618void QFont::setStretch(
int factor)
1620 if (factor < 0 || factor > 4000) {
1621 qWarning(
"QFont::setStretch: Parameter '%d' out of range", factor);
1625 if ((resolve_mask & QFont::StretchResolved) &&
1626 d->request.stretch == (uint)factor)
1631 d->request.stretch = (uint)factor;
1632 resolve_mask |= QFont::StretchResolved;
1636
1637
1638
1639
1640
1641
1642
1643
1646
1647
1648
1649
1650
1651qreal QFont::letterSpacing()
const
1653 return d->letterSpacing.toReal();
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668void QFont::setLetterSpacing(SpacingType type, qreal spacing)
1670 const QFixed newSpacing = QFixed::fromReal(spacing);
1671 const bool absoluteSpacing = type == AbsoluteSpacing;
1672 if ((resolve_mask & QFont::LetterSpacingResolved) &&
1673 d->letterSpacingIsAbsolute == absoluteSpacing &&
1674 d->letterSpacing == newSpacing)
1677 QFontPrivate::detachButKeepEngineData(
this);
1679 d->letterSpacing = newSpacing;
1680 d->letterSpacingIsAbsolute = absoluteSpacing;
1681 resolve_mask |= QFont::LetterSpacingResolved;
1685
1686
1687
1688
1689
1690QFont::SpacingType QFont::letterSpacingType()
const
1692 return d->letterSpacingIsAbsolute ? AbsoluteSpacing : PercentageSpacing;
1696
1697
1698
1699
1700
1701qreal QFont::wordSpacing()
const
1703 return d->wordSpacing.toReal();
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720void QFont::setWordSpacing(qreal spacing)
1722 const QFixed newSpacing = QFixed::fromReal(spacing);
1723 if ((resolve_mask & QFont::WordSpacingResolved) &&
1724 d->wordSpacing == newSpacing)
1727 QFontPrivate::detachButKeepEngineData(
this);
1729 d->wordSpacing = newSpacing;
1730 resolve_mask |= QFont::WordSpacingResolved;
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1748
1749
1750
1751
1752
1753
1754
1755void QFont::setCapitalization(Capitalization caps)
1757 if ((resolve_mask & QFont::CapitalizationResolved) &&
1758 capitalization() == caps)
1761 QFontPrivate::detachButKeepEngineData(
this);
1764 resolve_mask |= QFont::CapitalizationResolved;
1768
1769
1770
1771
1772
1773QFont::Capitalization QFont::capitalization()
const
1775 return static_cast<QFont::Capitalization> (d->capital);
1779
1780
1781
1782
1783
1784bool QFont::exactMatch()
const
1786 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
1787 Q_ASSERT(engine !=
nullptr);
1788 return d->request.exactMatch(engine->fontDef);
1792
1793
1794
1795
1796
1797
1798
1799
1800bool QFont::operator==(
const QFont &f)
const
1803 || (f.d->request == d->request
1804 && f.d->request.pointSize == d->request.pointSize
1805 && f.d->underline == d->underline
1806 && f.d->overline == d->overline
1807 && f.d->strikeOut == d->strikeOut
1808 && f.d->kerning == d->kerning
1809 && f.d->capital == d->capital
1810 && f.d->letterSpacingIsAbsolute == d->letterSpacingIsAbsolute
1811 && f.d->letterSpacing == d->letterSpacing
1812 && f.d->wordSpacing == d->wordSpacing
1813 && f.d->features == d->features
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829bool QFont::operator<(
const QFont &f)
const
1833 if (f.d == d)
return false;
1835 const QFontDef &r1 = f.d->request;
1836 const QFontDef &r2 = d->request;
1837 if (r1.pointSize != r2.pointSize)
return r1.pointSize < r2.pointSize;
1838 if (r1.pixelSize != r2.pixelSize)
return r1.pixelSize < r2.pixelSize;
1839 if (r1.weight != r2.weight)
return r1.weight < r2.weight;
1840 if (r1.style != r2.style)
return r1.style < r2.style;
1841 if (r1.stretch != r2.stretch)
return r1.stretch < r2.stretch;
1842 if (r1.styleHint != r2.styleHint)
return r1.styleHint < r2.styleHint;
1843 if (r1.styleStrategy != r2.styleStrategy)
return r1.styleStrategy < r2.styleStrategy;
1844 if (r1.families != r2.families)
return r1.families < r2.families;
1845 if (f.d->capital != d->capital)
return f.d->capital < d->capital;
1847 if (f.d->letterSpacingIsAbsolute != d->letterSpacingIsAbsolute)
return f.d->letterSpacingIsAbsolute < d->letterSpacingIsAbsolute;
1848 if (f.d->letterSpacing != d->letterSpacing)
return f.d->letterSpacing < d->letterSpacing;
1849 if (f.d->wordSpacing != d->wordSpacing)
return f.d->wordSpacing < d->wordSpacing;
1851 int f1attrs = (f.d->underline << 3) + (f.d->overline << 2) + (f.d->strikeOut<<1) + f.d->kerning;
1852 int f2attrs = (d->underline << 3) + (d->overline << 2) + (d->strikeOut<<1) + d->kerning;
1853 if (f1attrs != f2attrs)
return f1attrs < f2attrs;
1855 if (d->features != f.d->features) {
1856 return std::lexicographical_compare(f.d->features.keyValueBegin(), f.d->features.keyValueEnd(),
1857 d->features.keyValueBegin(), d->features.keyValueEnd());
1860 return std::lexicographical_compare(r1.variableAxisValues.keyValueBegin(), r1.variableAxisValues.keyValueEnd(),
1861 r2.variableAxisValues.keyValueBegin(), r2.variableAxisValues.keyValueEnd());
1866
1867
1868
1869
1870
1871
1872
1873
1874bool QFont::operator!=(
const QFont &f)
const
1876 return !(operator==(f));
1880
1881
1882QFont::operator QVariant()
const
1884 return QVariant::fromValue(*
this);
1888
1889
1890
1891
1892
1893
1894bool QFont::isCopyOf(
const QFont & f)
const
1900
1901
1902
1903QFont QFont::resolve(
const QFont &other)
const
1905 if (resolve_mask == 0 || (resolve_mask == other.resolve_mask && *
this == other)) {
1907 o.resolve_mask = resolve_mask;
1913 font.d->resolve(resolve_mask, other.d.data());
1919
1920
1921
1924
1925
1926
1930
1931
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947QString QFont::substitute(
const QString &familyName)
1949 QFontSubst *fontSubst = globalFontSubst();
1950 Q_ASSERT(fontSubst !=
nullptr);
1951 QFontSubst::ConstIterator it = fontSubst->constFind(familyName.toLower());
1952 if (it != fontSubst->constEnd() && !(*it).isEmpty())
1953 return (*it).first();
1960
1961
1962
1963
1964
1965
1966
1967
1968QStringList QFont::substitutes(
const QString &familyName)
1970 QFontSubst *fontSubst = globalFontSubst();
1971 Q_ASSERT(fontSubst !=
nullptr);
1972 return fontSubst->value(familyName.toLower(), QStringList());
1977
1978
1979
1980
1981
1982
1983
1984
1985void QFont::insertSubstitution(
const QString &familyName,
1986 const QString &substituteName)
1988 QFontSubst *fontSubst = globalFontSubst();
1989 Q_ASSERT(fontSubst !=
nullptr);
1990 QStringList &list = (*fontSubst)[familyName.toLower()];
1991 QString s = substituteName.toLower();
1992 if (!list.contains(s))
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007void QFont::insertSubstitutions(
const QString &familyName,
2008 const QStringList &substituteNames)
2010 QFontSubst *fontSubst = globalFontSubst();
2011 Q_ASSERT(fontSubst !=
nullptr);
2012 QStringList &list = (*fontSubst)[familyName.toLower()];
2013 for (
const QString &substituteName : substituteNames) {
2014 const QString lowerSubstituteName = substituteName.toLower();
2015 if (!list.contains(lowerSubstituteName))
2016 list.append(lowerSubstituteName);
2021
2022
2023
2024
2025
2026void QFont::removeSubstitutions(
const QString &familyName)
2028 QFontSubst *fontSubst = globalFontSubst();
2029 Q_ASSERT(fontSubst !=
nullptr);
2030 fontSubst->remove(familyName.toLower());
2034
2035
2036
2037
2038QStringList QFont::substitutions()
2040 QFontSubst *fontSubst = globalFontSubst();
2041 Q_ASSERT(fontSubst !=
nullptr);
2042 QStringList ret = fontSubst->keys();
2048#ifndef QT_NO_DATASTREAM
2050
2051
2052
2055 Q_ASSERT(f !=
nullptr);
2057 if (f->request.style)
2065 if (f->request.fixedPitch)
2069 if (version >= QDataStream::Qt_4_0) {
2073 if (f->request.style == QFont::StyleOblique)
2080 Q_ASSERT(f !=
nullptr);
2082 if (f->request.ignorePitch)
2084 if (f->letterSpacingIsAbsolute)
2090
2091
2092
2095 Q_ASSERT(f !=
nullptr);
2096 f->request.style = (bits & 0x01) != 0 ? QFont::StyleItalic : QFont::StyleNormal;
2097 f->underline = (bits & 0x02) != 0;
2098 f->overline = (bits & 0x40) != 0;
2099 f->strikeOut = (bits & 0x04) != 0;
2100 f->request.fixedPitch = (bits & 0x08) != 0;
2102 if (version >= QDataStream::Qt_4_0)
2103 f->kerning = (bits & 0x10) != 0;
2104 if ((bits & 0x80) != 0)
2105 f->request.style = QFont::StyleOblique;
2110 Q_ASSERT(f !=
nullptr);
2111 f->request.ignorePitch = (bits & 0x01) != 0;
2112 f->letterSpacingIsAbsolute = (bits & 0x02) != 0;
2117
2118
2119
2120
2121
2122QString QFont::key()
const
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155QString QFont::toString()
const
2157 const QChar comma(u',');
2158 QString fontDescription = family() + comma +
2159 QString::number( pointSizeF()) + comma +
2160 QString::number( pixelSize()) + comma +
2161 QString::number((
int) styleHint()) + comma +
2162 QString::number( weight()) + comma +
2163 QString::number((
int) style()) + comma +
2164 QString::number((
int) underline()) + comma +
2165 QString::number((
int) strikeOut()) + comma +
2166 QString::number((
int)fixedPitch()) + comma +
2167 QString::number((
int)
false) + comma +
2168 QString::number((
int)capitalization()) + comma +
2169 QString::number((
int)letterSpacingType()) + comma +
2170 QString::number(letterSpacing()) + comma +
2171 QString::number(wordSpacing()) + comma +
2172 QString::number(stretch()) + comma +
2173 QString::number((
int)styleStrategy()) + comma +
2176 fontDescription += comma + QString::number(d->features.size());
2177 for (
const auto &[tag, value] : std::as_const(d->features).asKeyValueRange())
2178 fontDescription += comma + QLatin1StringView{tag.toString()} + u'=' + QString::number(value);
2180 fontDescription += comma + QString::number(d->request.variableAxisValues.size());
2181 for (
const auto &[tag, value] : std::as_const(d->request.variableAxisValues).asKeyValueRange())
2182 fontDescription += comma + QLatin1StringView{tag.toString()} + u'=' + QString::number(value);
2184 return fontDescription;
2188
2189
2190
2191
2194 return qHash(QFontPrivate::get(font)->request, seed);
2199 const int separator = view.indexOf(u'=');
2200 if (separator == -1)
2201 return std::nullopt;
2203 const std::optional<QFont::Tag> tag = QFont::Tag::fromString(view.sliced(0, separator));
2205 return std::nullopt;
2207 bool valueOk =
false;
2208 const quint32 value = view.sliced(separator + 1).toUInt(&valueOk);
2210 return std::nullopt;
2212 return std::make_pair(*tag, value);
2217 const int separator = view.indexOf(u'=');
2218 if (separator == -1)
2219 return std::nullopt;
2221 const std::optional<QFont::Tag> tag = QFont::Tag::fromString(view.sliced(0, separator));
2223 return std::nullopt;
2225 bool valueOk =
false;
2226 const float value = view.sliced(separator + 1).toFloat(&valueOk);
2228 return std::nullopt;
2230 return std::make_pair(*tag, value);
2234
2235
2236
2237
2238
2239
2240bool QFont::fromString(
const QString &descrip)
2242 const auto sr = QStringView(descrip).trimmed();
2243 const auto l = sr.split(u',');
2244 const int count = l.size();
2245 if (!count || (count > 2 && count < 10) || l.first().isEmpty()) {
2246 qWarning(
"QFont::fromString: Invalid description '%s'",
2247 descrip.isEmpty() ?
"(empty)" : descrip.toLatin1().data());
2251 setFamily(l[0].toString());
2252 if (count > 1 && l[1].toDouble() > 0.0)
2253 setPointSizeF(l[1].toDouble());
2256 if (l[2].toInt() > 0)
2257 setPixelSize(l[2].toInt());
2258 setStyleHint((StyleHint) l[3].toInt());
2260 setWeight(QFont::Weight(l[4].toInt()));
2262 setWeight(QFont::Weight(qt_legacyToOpenTypeWeight(l[4].toInt())));
2263 setStyle((QFont::Style)l[5].toInt());
2264 setUnderline(l[6].toInt());
2265 setStrikeOut(l[7].toInt());
2266 setFixedPitch(l[8].toInt());
2267 if (!d->request.fixedPitch)
2268 d->request.ignorePitch =
true;
2270 setCapitalization((Capitalization)l[10].toInt());
2271 setLetterSpacing((SpacingType)l[11].toInt(), l[12].toDouble());
2272 setWordSpacing(l[13].toDouble());
2273 setStretch(l[14].toInt());
2274 setStyleStrategy((StyleStrategy)l[15].toInt());
2278 d->request.styleName = l[10].toString();
2279 else if (count >= 17)
2280 d->request.styleName = l[16].toString();
2282 d->request.styleName.clear();
2285 clearVariableAxes();
2288 if (position >= count)
2291 const int featureCount = l[position++].toInt();
2292 if (position + featureCount > count)
2295 for (
int i = 0; i < featureCount; ++i) {
2296 if (
const auto feature = fontFeatureFromString(l[position++]))
2297 setFeature(feature->first, feature->second);
2300 if (position >= count)
2303 const int variableAxisCount = l[position++].toInt();
2304 if (position + variableAxisCount > count)
2307 for (
int i = 0; i < variableAxisCount; ++i) {
2308 if (
const auto axis = variableAxisFromString(l[position++]))
2309 setVariableAxis(axis->first, axis->second);
2317
2318
2319
2320
2321
2322
2323void QFont::initialize()
2328
2329
2330
2331
2332void QFont::cleanup()
2334 QFontCache::cleanup();
2338
2339
2340
2341void QFont::cacheStatistics()
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2376
2377
2378
2379
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2395
2396
2397
2398
2399
2402
2403
2404
2407
2408
2409
2410
2411
2412
2415
2416
2417
2418
2419
2420
2423
2424
2425
2426
2427
2428
2431
2432
2433
2434
2435
2436
2437
2440
2441
2442
2443
2444
2445
2446
2447
2448std::optional<QFont::Tag> QFont::Tag::fromString(QAnyStringView view)
noexcept
2450 if (view.size() != 4) {
2451 qWarning(
"The tag name must be exactly 4 characters long!");
2452 return std::nullopt;
2454 const QFont::Tag maybeTag = view.visit([](
auto view) {
2455 using CharType =
decltype(view.at(0));
2456 if constexpr (std::is_same_v<CharType,
char>) {
2457 const char bytes[5] = { view.at(0), view.at(1), view.at(2), view.at(3), 0 };
2460 const char bytes[5] = { view.at(0).toLatin1(), view.at(1).toLatin1(),
2461 view.at(2).toLatin1(), view.at(3).toLatin1(), 0 };
2465 return maybeTag.isValid() ? std::optional<Tag>(maybeTag) : std::nullopt;
2469
2470
2471
2472
2473
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513void QFont::setVariableAxis(Tag tag,
float value)
2515 if (tag.isValid()) {
2516 if (resolve_mask & QFont::VariableAxesResolved && d->hasVariableAxis(tag, value))
2521 d->setVariableAxis(tag, value);
2522 resolve_mask |= QFont::VariableAxesResolved;
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536void QFont::unsetVariableAxis(Tag tag)
2538 if (tag.isValid()) {
2541 d->unsetVariableAxis(tag);
2542 resolve_mask |= QFont::VariableAxesResolved;
2547
2548
2549
2550
2551
2552
2553
2554
2555QList<QFont::Tag> QFont::variableAxisTags()
const
2557 return d->request.variableAxisValues.keys();
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570float QFont::variableAxisValue(Tag tag)
const
2572 return d->request.variableAxisValues.value(tag);
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585bool QFont::isVariableAxisSet(Tag tag)
const
2587 return d->request.variableAxisValues.contains(tag);
2591
2592
2593
2594
2595
2596
2597
2598
2599void QFont::clearVariableAxes()
2601 if (d->request.variableAxisValues.isEmpty())
2605 d->request.variableAxisValues.clear();
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645void QFont::setFeature(Tag tag, quint32 value)
2647 if (tag.isValid()) {
2648 d->detachButKeepEngineData(
this);
2649 d->setFeature(tag, value);
2650 resolve_mask |= QFont::FeaturesResolved;
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669void QFont::unsetFeature(Tag tag)
2671 if (tag.isValid()) {
2672 d->detachButKeepEngineData(
this);
2673 d->unsetFeature(tag);
2674 resolve_mask |= QFont::FeaturesResolved;
2679
2680
2681
2682
2683
2684
2685
2686
2687QList<QFont::Tag> QFont::featureTags()
const
2689 return d->features.keys();
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702quint32 QFont::featureValue(Tag tag)
const
2704 return d->features.value(tag);
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717bool QFont::isFeatureSet(Tag tag)
const
2719 return d->features.contains(tag);
2723
2724
2725
2726
2727
2728
2729
2730
2731void QFont::clearFeatures()
2733 if (d->features.isEmpty())
2736 d->detachButKeepEngineData(
this);
2737 d->features.clear();
2742 QFont::StyleHint styleHint,
2743 QFontDatabasePrivate::ExtendedScript script);
2746
2747
2748
2749
2750
2751
2752
2753QString QFont::defaultFamily()
const
2755 const QStringList fallbacks = qt_fallbacksForFamily(QString(),
2757 QFont::StyleHint(d->request.styleHint),
2758 QFontDatabasePrivate::Script_Common);
2759 if (!fallbacks.isEmpty())
2760 return fallbacks.first();
2765
2766
2767
2768
2769
2770
2771
2772
2774QStringList QFont::families()
const
2776 return d->request.families;
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2796void QFont::setFamilies(
const QStringList &families)
2798 if ((resolve_mask & QFont::FamiliesResolved) && d->request.families == families)
2801 d->request.families = families;
2802 resolve_mask |= QFont::FamiliesResolved;
2807
2808
2809#ifndef QT_NO_DATASTREAM
2812
2813
2814
2815
2816
2817
2818
2819QDataStream &operator<<(QDataStream &s,
const QFont &font)
2821 if (s.version() == 1) {
2822 s << font.d->request.families.constFirst().toLatin1();
2824 s << font.d->request.families.constFirst();
2825 if (s.version() >= QDataStream::Qt_5_4)
2826 s << font.d->request.styleName;
2829 if (s.version() >= QDataStream::Qt_4_0) {
2831 double pointSize = font.d->request.pointSize;
2832 qint32 pixelSize = font.d->request.pixelSize;
2835 }
else if (s.version() <= 3) {
2836 qint16 pointSize = (qint16) (font.d->request.pointSize * 10);
2837 if (pointSize < 0) {
2838 pointSize = (qint16)QFontInfo(font).pointSize() * 10;
2842 s << (qint16) (font.d->request.pointSize * 10);
2843 s << (qint16) font.d->request.pixelSize;
2846 s << (quint8) font.d->request.styleHint;
2847 if (s.version() >= QDataStream::Qt_3_1) {
2850 if (s.version() >= QDataStream::Qt_5_4)
2851 s << (quint16) font.d->request.styleStrategy;
2853 s << (quint8) font.d->request.styleStrategy;
2856 if (s.version() < QDataStream::Qt_6_0)
2857 s << quint8(0) << quint8(qt_openTypeToLegacyWeight(font.d->request.weight));
2859 s << quint16(font.d->request.weight);
2861 s << get_font_bits(s.version(), font.d.data());
2862 if (s.version() >= QDataStream::Qt_4_3)
2863 s << (quint16)font.d->request.stretch;
2864 if (s.version() >= QDataStream::Qt_4_4)
2865 s << get_extended_font_bits(font.d.data());
2866 if (s.version() >= QDataStream::Qt_4_5) {
2867 s << font.d->letterSpacing.value();
2868 s << font.d->wordSpacing.value();
2870 if (s.version() >= QDataStream::Qt_5_4)
2871 s << (quint8)font.d->request.hintingPreference;
2872 if (s.version() >= QDataStream::Qt_5_6)
2873 s << (quint8)font.d->capital;
2874 if (s.version() >= QDataStream::Qt_5_13) {
2875 if (s.version() < QDataStream::Qt_6_0)
2876 s << font.d->request.families.mid(1);
2878 s << font.d->request.families;
2880 if (s.version() >= QDataStream::Qt_6_6)
2881 s << font.d->features;
2882 if (s.version() >= QDataStream::Qt_6_7)
2883 s << font.d->request.variableAxisValues;
2889
2890
2891
2892
2893
2894
2895
2898 font.d =
new QFontPrivate;
2899 font.resolve_mask = QFont::AllPropertiesResolved;
2901 quint8 styleHint, bits;
2902 quint16 styleStrategy = QFont::PreferDefault;
2904 if (s.version() == 1) {
2907 font.d->request.families = QStringList(QString::fromLatin1(fam));
2911 font.d->request.families = QStringList(fam);
2912 if (s.version() >= QDataStream::Qt_5_4)
2913 s >> font.d->request.styleName;
2916 if (s.version() >= QDataStream::Qt_4_0) {
2922 font.d->request.pointSize = qreal(pointSize);
2923 font.d->request.pixelSize = pixelSize;
2925 qint16 pointSize, pixelSize = -1;
2927 if (s.version() >= 4)
2929 font.d->request.pointSize = qreal(pointSize / 10.);
2930 font.d->request.pixelSize = pixelSize;
2933 if (s.version() >= QDataStream::Qt_3_1) {
2934 if (s.version() >= QDataStream::Qt_5_4) {
2937 quint8 tempStyleStrategy;
2938 s >> tempStyleStrategy;
2939 styleStrategy = tempStyleStrategy;
2943 if (s.version() < QDataStream::Qt_6_0) {
2948 font.d->request.weight = qt_legacyToOpenTypeWeight(weight);
2952 font.d->request.weight = weight;
2957 font.d->request.styleHint = styleHint;
2958 font.d->request.styleStrategy = styleStrategy;
2960 set_font_bits(s.version(), bits, font.d.data());
2962 if (s.version() >= QDataStream::Qt_4_3) {
2965 font.d->request.stretch = stretch;
2968 if (s.version() >= QDataStream::Qt_4_4) {
2969 quint8 extendedBits;
2971 set_extended_font_bits(extendedBits, font.d.data());
2973 if (s.version() >= QDataStream::Qt_4_5) {
2976 font.d->letterSpacing.setValue(value);
2978 font.d->wordSpacing.setValue(value);
2980 if (s.version() >= QDataStream::Qt_5_4) {
2983 font.d->request.hintingPreference = QFont::HintingPreference(value);
2985 if (s.version() >= QDataStream::Qt_5_6) {
2988 font.d->capital = QFont::Capitalization(value);
2990 if (s.version() >= QDataStream::Qt_5_13) {
2993 if (s.version() < QDataStream::Qt_6_0)
2994 font.d->request.families.append(value);
2996 font.d->request.families = value;
2998 if (s.version() >= QDataStream::Qt_6_6) {
2999 font.d->features.clear();
3000 s >> font.d->features;
3002 if (s.version() >= QDataStream::Qt_6_7) {
3003 font.d->request.variableAxisValues.clear();
3004 s >> font.d->request.variableAxisValues;
3010QDataStream &operator<<(QDataStream &stream, QFont::Tag tag)
3012 stream << tag.value();
3016QDataStream &
operator>>(QDataStream &stream, QFont::Tag &tag)
3020 if (
const auto maybeTag = QFont::Tag::fromValue(value))
3023 stream.setStatus(QDataStream::ReadCorruptData);
3031
3032
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125QFontInfo::QFontInfo(
const QFont &font)
3131
3132
3133QFontInfo::QFontInfo(
const QFontInfo &fi)
3139
3140
3141QFontInfo::~QFontInfo()
3146
3147
3148QFontInfo &QFontInfo::operator=(
const QFontInfo &fi)
3155
3156
3157
3158
3161
3162
3163
3164
3165QString QFontInfo::family()
const
3167 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3168 Q_ASSERT(engine !=
nullptr);
3169 return engine->fontDef.families.isEmpty() ? QString() : engine->fontDef.families.constFirst();
3173
3174
3175
3176
3177
3178
3179
3180QString QFontInfo::styleName()
const
3182 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3183 Q_ASSERT(engine !=
nullptr);
3184 return engine->fontDef.styleName;
3188
3189
3190
3191
3192int QFontInfo::pointSize()
const
3194 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3195 Q_ASSERT(engine !=
nullptr);
3196 return qRound(engine->fontDef.pointSize);
3200
3201
3202
3203
3204qreal QFontInfo::pointSizeF()
const
3206 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3207 Q_ASSERT(engine !=
nullptr);
3208 return engine->fontDef.pointSize;
3212
3213
3214
3215
3216int QFontInfo::pixelSize()
const
3218 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3219 Q_ASSERT(engine !=
nullptr);
3220 return engine->fontDef.pixelSize;
3224
3225
3226
3227
3228bool QFontInfo::italic()
const
3230 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3231 Q_ASSERT(engine !=
nullptr);
3232 return engine->fontDef.style != QFont::StyleNormal;
3236
3237
3238
3239
3240QFont::Style QFontInfo::style()
const
3242 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3243 Q_ASSERT(engine !=
nullptr);
3244 return (QFont::Style)engine->fontDef.style;
3248#if QT_DEPRECATED_SINCE(6
, 0
)
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262int QFontInfo::legacyWeight()
const
3264 return qt_openTypeToLegacyWeight(weight());
3270
3271
3272
3273
3274int QFontInfo::weight()
const
3276 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3277 Q_ASSERT(engine !=
nullptr);
3278 return engine->fontDef.weight;
3283
3284
3285
3286
3287
3288
3289
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301bool QFontInfo::underline()
const
3303 return d->underline;
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316bool QFontInfo::overline()
const
3322
3323
3324
3325
3326
3327
3328
3329bool QFontInfo::strikeOut()
const
3331 return d->strikeOut;
3335
3336
3337
3338
3339bool QFontInfo::fixedPitch()
const
3341 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3342 Q_ASSERT(engine !=
nullptr);
3344 if (!engine->fontDef.fixedPitchComputed) {
3345 QChar ch[2] = { u'i', u'm' };
3346 QGlyphLayoutArray<2> g;
3348 if (engine->stringToCMap(ch, 2, &g, &l, {}) < 0)
3351 engine->fontDef.fixedPitch = g.advances[0] == g.advances[1];
3352 engine->fontDef.fixedPitchComputed =
true;
3355 return engine->fontDef.fixedPitch;
3359
3360
3361
3362
3363
3364
3365QFont::StyleHint QFontInfo::styleHint()
const
3367 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3368 Q_ASSERT(engine !=
nullptr);
3369 return (QFont::StyleHint) engine->fontDef.styleHint;
3373
3374
3375
3376
3377
3378bool QFontInfo::exactMatch()
const
3380 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3381 Q_ASSERT(engine !=
nullptr);
3382 return d->request.exactMatch(engine->fontDef);
3386
3387
3388
3389
3390
3391
3392
3393QList<QFontVariableAxis> QFontInfo::variableAxes()
const
3395 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3396 Q_ASSERT(engine !=
nullptr);
3397 return engine->variableAxes();
3405using namespace std::chrono_literals;
3407#ifdef QFONTCACHE_DEBUG
3409static constexpr auto fast_timeout = 1s;
3410static constexpr auto slow_timeout = 5s;
3416#ifndef QFONTCACHE_MIN_COST
3417# define QFONTCACHE_MIN_COST 4
*1024
3422QFontCache *QFontCache::instance()
3424 QFontCache *&fontCache = theFontCache()->localData();
3426 fontCache =
new QFontCache;
3430void QFontCache::cleanup()
3432 QThreadStorage<QFontCache *> *cache =
nullptr;
3434 cache = theFontCache();
3435 } QT_CATCH (
const std::bad_alloc &) {
3438 if (cache && cache->hasLocalData())
3439 cache->setLocalData(
nullptr);
3442Q_CONSTINIT
static QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(0);
3444QFontCache::QFontCache()
3445 : QObject(), total_cost(0), max_cost(min_cost),
3446 current_timestamp(0), fast(
false),
3447 autoClean(QGuiApplication::instance()
3448 && (QGuiApplication::instance()->thread() == QThread::currentThread())),
3449 m_id(font_cache_id.fetchAndAddRelaxed(1) + 1)
3453QFontCache::~QFontCache()
3458void QFontCache::clear()
3461 EngineDataCache::Iterator it = engineDataCache.begin(),
3462 end = engineDataCache.end();
3464 QFontEngineData *data = it.value();
3465 for (
int i = 0; i < QFontDatabasePrivate::ScriptCount; ++i) {
3466 if (data->engines[i]) {
3467 if (!data->engines[i]->ref.deref()) {
3468 Q_ASSERT(engineCacheCount.value(data->engines[i]) == 0);
3469 delete data->engines[i];
3471 data->engines[i] =
nullptr;
3474 if (!data->ref.deref()) {
3477 FC_DEBUG(
"QFontCache::clear: engineData %p still has refcount %d",
3478 data, data->ref.loadRelaxed());
3484 engineDataCache.clear();
3487 bool mightHaveEnginesLeftForCleanup;
3489 mightHaveEnginesLeftForCleanup =
false;
3490 for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
3492 QFontEngine *engine = it.value().data;
3494 const int cacheCount = --engineCacheCount[engine];
3495 Q_ASSERT(cacheCount >= 0);
3496 if (!engine->ref.deref()) {
3497 Q_ASSERT(cacheCount == 0);
3498 mightHaveEnginesLeftForCleanup = engine->type() == QFontEngine::Multi;
3500 }
else if (cacheCount == 0) {
3501 FC_DEBUG(
"QFontCache::clear: engine %p still has refcount %d",
3502 engine, engine->ref.loadRelaxed());
3504 it.value().data =
nullptr;
3507 }
while (mightHaveEnginesLeftForCleanup);
3509 engineCache.clear();
3510 engineCacheCount.clear();
3514 max_cost = min_cost;
3518QFontEngineData *QFontCache::findEngineData(
const QFontDef &def)
const
3520 EngineDataCache::ConstIterator it = engineDataCache.constFind(def);
3521 if (it == engineDataCache.constEnd())
3528void QFontCache::insertEngineData(
const QFontDef &def, QFontEngineData *engineData)
3530#ifdef QFONTCACHE_DEBUG
3531 FC_DEBUG(
"QFontCache: inserting new engine data %p", engineData);
3532 if (engineDataCache.contains(def)) {
3533 FC_DEBUG(
" QFontCache already contains engine data %p for key=(%g %g %d %d %d)",
3534 engineDataCache.value(def), def.pointSize,
3535 def.pixelSize, def.weight, def.style, def.fixedPitch);
3538 Q_ASSERT(!engineDataCache.contains(def));
3540 engineData->ref.ref();
3545 engineDataCache.insert(def, engineData);
3546 increaseCost(
sizeof(QFontEngineData));
3549QFontEngine *QFontCache::findEngine(
const Key &key)
3551 EngineCache::Iterator it = engineCache.find(key),
3552 end = engineCache.end();
3553 if (it == end)
return nullptr;
3555 Q_ASSERT(it.value().data !=
nullptr);
3556 Q_ASSERT(key.multi == (it.value().data->type() == QFontEngine::Multi));
3559 updateHitCountAndTimeStamp(it.value());
3561 return it.value().data;
3564void QFontCache::updateHitCountAndTimeStamp(Engine &value)
3567 value.timestamp = ++current_timestamp;
3569 FC_DEBUG(
"QFontCache: found font engine\n"
3570 " %p: timestamp %4u hits %3u ref %2d/%2d, type %d",
3571 value.data, value.timestamp, value.hits,
3572 value.data->ref.loadRelaxed(), engineCacheCount.value(value.data),
3573 value.data->type());
3576void QFontCache::insertEngine(
const Key &key, QFontEngine *engine,
bool insertMulti)
3578 Q_ASSERT(engine !=
nullptr);
3579 Q_ASSERT(key.multi == (engine->type() == QFontEngine::Multi));
3581#ifdef QFONTCACHE_DEBUG
3582 FC_DEBUG(
"QFontCache: inserting new engine %p, refcount %d", engine, engine->ref.loadRelaxed());
3583 if (!insertMulti && engineCache.contains(key)) {
3584 FC_DEBUG(
" QFontCache already contains engine %p for key=(%g %g %d %d %d)",
3585 engineCache.value(key).data, key.def.pointSize,
3586 key.def.pixelSize, key.def.weight, key.def.style, key.def.fixedPitch);
3594 Engine data(engine);
3595 data.timestamp = ++current_timestamp;
3598 engineCache.insert(key, data);
3600 engineCache.replace(key, data);
3602 if (++engineCacheCount[engine] == 1)
3603 increaseCost(engine->cache_cost);
3606void QFontCache::increaseCost(uint cost)
3608 cost = (cost + 512) / 1024;
3609 cost = cost > 0 ? cost : 1;
3612 FC_DEBUG(
" COST: increased %u kb, total_cost %u kb, max_cost %u kb",
3613 cost, total_cost, max_cost);
3615 if (total_cost > max_cost) {
3616 max_cost = total_cost;
3621 if (!timer.isActive() || ! fast) {
3622 FC_DEBUG(
" TIMER: starting fast timer (%d s)",
static_cast<
int>(fast_timeout.count()));
3624 timer.start(fast_timeout,
this);
3630void QFontCache::decreaseCost(uint cost)
3632 cost = (cost + 512) / 1024;
3633 cost = cost > 0 ? cost : 1;
3634 Q_ASSERT(cost <= total_cost);
3637 FC_DEBUG(
" COST: decreased %u kb, total_cost %u kb, max_cost %u kb",
3638 cost, total_cost, max_cost);
3641void QFontCache::timerEvent(QTimerEvent *)
3643 FC_DEBUG(
"QFontCache::timerEvent: performing cache maintenance (timestamp %u)",
3646 if (total_cost <= max_cost && max_cost <= min_cost) {
3647 FC_DEBUG(
" cache redused sufficiently, stopping timer");
3657void QFontCache::decreaseCache()
3660 uint in_use_cost = 0;
3666 const uint engine_data_cost =
3667 sizeof(QFontEngineData) > 1024 ?
sizeof(QFontEngineData) : 1024;
3669 EngineDataCache::ConstIterator it = engineDataCache.constBegin(),
3670 end = engineDataCache.constEnd();
3671 for (; it != end; ++it) {
3672 FC_DEBUG(
" %p: ref %2d", it.value(),
int(it.value()->ref.loadRelaxed()));
3674 if (it.value()->ref.loadRelaxed() != 1)
3675 in_use_cost += engine_data_cost;
3682 EngineCache::ConstIterator it = engineCache.constBegin(),
3683 end = engineCache.constEnd();
3684 for (; it != end; ++it) {
3685 const auto useCount = engineCacheCount.value(it.value().data);
3686 const auto refCount = it.value().data->ref.loadRelaxed();
3687 const auto cacheCost = it.value().data->cache_cost;
3689 FC_DEBUG(
" %p: timestamp %4u hits %2u ref %2d/%2d, cost %u bytes",
3690 it.value().data, it.value().timestamp, it.value().hits,
3691 refCount, useCount, cacheCost);
3693 Q_ASSERT(useCount > 0);
3694 if (useCount > 0 && refCount > useCount)
3695 in_use_cost += cacheCost / useCount;
3699 in_use_cost += engineCache.size();
3702 in_use_cost = (in_use_cost + 512) / 1024;
3705
3706
3707
3708
3709
3710
3711
3712 uint new_max_cost = qMax(qMax(max_cost / 2, in_use_cost), min_cost);
3714 FC_DEBUG(
" after sweep, in use %u kb, total %u kb, max %u kb, new max %u kb",
3715 in_use_cost, total_cost, max_cost, new_max_cost);
3718 if (new_max_cost == max_cost) {
3720 FC_DEBUG(
" cannot shrink cache, slowing timer");
3722 if (timer.isActive()) {
3723 timer.start(slow_timeout,
this);
3728 }
else if (! fast) {
3729 FC_DEBUG(
" dropping into passing gear");
3731 timer.start(fast_timeout,
this);
3736 max_cost = new_max_cost;
3742 EngineDataCache::Iterator it = engineDataCache.begin();
3743 while (it != engineDataCache.end()) {
3744 if (it.value()->ref.loadRelaxed() == 1) {
3746 decreaseCost(
sizeof(QFontEngineData));
3747 it.value()->ref.deref();
3749 it = engineDataCache.erase(it);
3759 bool cost_decreased;
3761 cost_decreased =
false;
3763 EngineCache::Iterator it = engineCache.begin(),
3764 end = engineCache.end();
3767 uint least_popular = ~0u;
3769 EngineCache::Iterator jt = end;
3771 for ( ; it != end; ++it) {
3772 if (it.value().data->ref.loadRelaxed() != engineCacheCount.value(it.value().data))
3775 if (it.value().timestamp < oldest && it.value().hits <= least_popular) {
3776 oldest = it.value().timestamp;
3777 least_popular = it.value().hits;
3784 FC_DEBUG(
" %p: timestamp %4u hits %2u ref %2d/%2d, type %d",
3785 it.value().data, it.value().timestamp, it.value().hits,
3786 it.value().data->ref.loadRelaxed(), engineCacheCount.value(it.value().data),
3787 it.value().data->type());
3789 QFontEngine *fontEngine = it.value().data;
3791 it = engineCache.begin();
3792 while (it != engineCache.end()) {
3793 if (it.value().data == fontEngine) {
3794 fontEngine->ref.deref();
3795 it = engineCache.erase(it);
3801 Q_ASSERT(fontEngine->ref.loadRelaxed() == 0);
3802 decreaseCost(fontEngine->cache_cost);
3804 engineCacheCount.remove(fontEngine);
3806 cost_decreased =
true;
3808 }
while (cost_decreased && total_cost > max_cost);
3812#ifndef QT_NO_DEBUG_STREAM
3813QDebug operator<<(QDebug stream,
const QFont &font)
3815 QDebugStateSaver saver(stream);
3816 stream.nospace().noquote();
3819 if (stream.verbosity() == QDebug::DefaultVerbosity) {
3820 stream << font.toString() <<
")";
3824 QString fontDescription;
3825 QDebug debug(&fontDescription);
3828 const QFont defaultFont(
new QFontPrivate);
3830 for (
int property = QFont::SizeResolved; property < QFont::AllPropertiesResolved; property <<= 1) {
3831 const bool resolved = (font.resolve_mask & property) != 0;
3832 if (!resolved && stream.verbosity() == QDebug::MinimumVerbosity)
3835 #define QFONT_DEBUG_SKIP_DEFAULT(prop)
3836 if ((font.prop() == defaultFont.prop()) && stream.verbosity() == 1
)
3839 QDebugStateSaver saver(debug);
3842 case QFont::SizeResolved:
3843 if (font.pointSizeF() >= 0)
3844 debug << font.pointSizeF() <<
"pt";
3845 else if (font.pixelSize() >= 0)
3846 debug << font.pixelSize() <<
"px";
3850 case QFont::StyleHintResolved:
3852 debug.verbosity(1) << font.styleHint();
break;
3853 case QFont::StyleStrategyResolved:
3855 debug.verbosity(1) << font.styleStrategy();
break;
3856 case QFont::WeightResolved:
3857 debug.verbosity(1) << QFont::Weight(font.weight());
break;
3858 case QFont::StyleResolved:
3860 debug.verbosity(0) << font.style();
break;
3861 case QFont::UnderlineResolved:
3863 debug <<
"underline=" << font.underline();
break;
3864 case QFont::OverlineResolved:
3866 debug <<
"overline=" << font.overline();
break;
3867 case QFont::StrikeOutResolved:
3869 debug <<
"strikeOut=" << font.strikeOut();
break;
3870 case QFont::FixedPitchResolved:
3872 debug <<
"fixedPitch=" << font.fixedPitch();
break;
3873 case QFont::StretchResolved:
3875 debug.verbosity(0) << QFont::Stretch(font.stretch());
break;
3876 case QFont::KerningResolved:
3878 debug <<
"kerning=" << font.kerning();
break;
3879 case QFont::CapitalizationResolved:
3881 debug.verbosity(0) << font.capitalization();
break;
3882 case QFont::LetterSpacingResolved:
3884 debug <<
"letterSpacing=" << font.letterSpacing();
3885 debug.verbosity(0) <<
" (" << font.letterSpacingType() <<
")";
3887 case QFont::HintingPreferenceResolved:
3889 debug.verbosity(0) << font.hintingPreference();
break;
3890 case QFont::StyleNameResolved:
3892 debug <<
"styleName=" << font.styleName();
break;
3897 #undef QFONT_DEBUG_SKIP_DEFAULT
3902 if (stream.verbosity() > QDebug::MinimumVerbosity)
3903 debug.verbosity(0) <<
"resolveMask=" << QFlags<QFont::ResolveProperties>(font.resolve_mask);
3905 fontDescription.chop(2);
3907 stream << fontDescription <<
')';
3912QDebug operator<<(QDebug debug, QFont::Tag tag)
3914 QDebugStateSaver saver(debug);
3915 debug.noquote() << tag.toString();
3922#include "moc_qfont.cpp"
QDataStream & operator>>(QDataStream &s, QKeyCombination &combination)
Q_GUI_EXPORT int qt_openTypeToLegacyWeight(int weight)
static void set_font_bits(int version, quint8 bits, QFontPrivate *f)
QRecursiveMutex * qt_fontdatabase_mutex()
static int convertWeights(int weight, bool inverted)
static QStringList splitIntoFamilies(const QString &family)
Q_GUI_EXPORT int qt_legacyToOpenTypeWeight(int weight)
static constexpr auto fast_timeout
static std::optional< std::pair< QFont::Tag, quint32 > > fontFeatureFromString(QStringView view)
QHash< QString, QStringList > QFontSubst
#define QFONT_DEBUG_SKIP_DEFAULT(prop)
#define QFONTCACHE_MIN_COST
#define QT_FONT_ENGINE_FROM_DATA(data, script)
Q_GUI_EXPORT int qt_defaultDpiY()
static constexpr auto slow_timeout
Q_GUI_EXPORT int qt_defaultDpiX()
static quint8 get_extended_font_bits(const QFontPrivate *f)
Q_GUI_EXPORT int qt_defaultDpi()
#define QFONTCACHE_DECREASE_TRIGGER_LIMIT
QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script)
static std::optional< std::pair< QFont::Tag, float > > variableAxisFromString(QStringView view)
static quint8 get_font_bits(int version, const QFontPrivate *f)
static void set_extended_font_bits(quint8 bits, QFontPrivate *f)
Q_CONSTINIT Q_GUI_EXPORT bool qt_is_tty_app
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
constexpr size_t qHash(const QSize &s, size_t seed=0) noexcept
bool exactMatch(const QFontDef &other) const