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
50QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QFontPrivate)
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 if (pixelSize != -1 && other.pixelSize != -1) {
73 if (pixelSize != other.pixelSize)
75 }
else if (pointSize != -1 && other.pointSize != -1) {
76 if (pointSize != other.pointSize)
88 if (families.size() != other.families.size())
91 QString this_family, this_foundry, other_family, other_foundry;
92 for (
int i = 0; i < families.size(); ++i) {
93 QFontDatabasePrivate::parseFontName(families.at(i), this_foundry, this_family);
94 QFontDatabasePrivate::parseFontName(other.families.at(i), other_foundry, other_family);
95 if (this_family != other_family || this_foundry != other_foundry)
99 if (variableAxisValues != other.variableAxisValues)
102 return (styleHint == other.styleHint
103 && styleStrategy == other.styleStrategy
104 && weight == other.weight
105 && style == other.style
106 && this_family == other_family
107 && (styleName.isEmpty() || other.styleName.isEmpty() || styleName == other.styleName)
108 && (this_foundry.isEmpty()
109 || other_foundry.isEmpty()
110 || this_foundry == other_foundry)
116Q_GUI_EXPORT QPoint qt_defaultDpis()
118 if (QCoreApplication::instance()->testAttribute(Qt::AA_Use96Dpi))
119 return QPoint(96, 96);
122 return QPoint(75, 75);
124 int dpis = QGuiApplicationPrivate::m_primaryScreenDpis.loadRelaxed();
125 int dpiX = (dpis >> 16) & 0xffff;
126 int dpiY = dpis & 0xffff;
127 if (dpiX > 0 && dpiY > 0)
128 return QPoint(dpiX, dpiY);
131 return QPoint(100, 100);
136 return qt_defaultDpis().x();
141 return qt_defaultDpis().y();
146 return qt_defaultDpiY();
152 static constexpr std::array<
int, 2> legacyToOpenTypeMap[] = {
153 { 0, QFont::Thin }, { 12, QFont::ExtraLight }, { 25, QFont::Light },
154 { 50, QFont::Normal }, { 57, QFont::Medium }, { 63, QFont::DemiBold },
155 { 75, QFont::Bold }, { 81, QFont::ExtraBold }, { 87, QFont::Black },
158 int closestDist = INT_MAX;
162 for (
auto mapping : legacyToOpenTypeMap) {
163 const int weightOld = mapping[ inverted];
164 const int weightNew = mapping[!inverted];
165 const int dist = qAbs(weightOld - weight);
166 if (dist < closestDist) {
181 QStringList familyList;
182 if (family.isEmpty())
184 const auto list = QStringView{family}.split(u',');
185 const int numFamilies = list.size();
186 familyList.reserve(numFamilies);
187 for (
int i = 0; i < numFamilies; ++i) {
188 auto str = list.at(i).trimmed();
189 if ((str.startsWith(u'"') && str.endsWith(u'"'))
190 || (str.startsWith(u'\'') && str.endsWith(u'\''))) {
191 str = str.mid(1, str.size() - 2);
193 familyList << str.toString();
210QFontPrivate::QFontPrivate()
211 : engineData(
nullptr), dpi(qt_defaultDpi()),
212 underline(
false), overline(
false), strikeOut(
false), kerning(
true),
213 capital(0), letterSpacingIsAbsolute(
false), scFont(
nullptr)
217QFontPrivate::QFontPrivate(
const QFontPrivate &other)
218 : request(other.request), engineData(
nullptr), dpi(other.dpi),
219 underline(other.underline), overline(other.overline),
220 strikeOut(other.strikeOut), kerning(other.kerning),
221 capital(other.capital), letterSpacingIsAbsolute(other.letterSpacingIsAbsolute),
222 letterSpacing(other.letterSpacing), wordSpacing(other.wordSpacing),
223 features(other.features), scFont(other.scFont)
225 if (scFont && scFont !=
this)
229QFontPrivate::~QFontPrivate()
231 if (engineData && !engineData->ref.deref())
233 engineData =
nullptr;
234 if (scFont && scFont !=
this) {
235 if (!scFont->ref.deref())
243#define QT_FONT_ENGINE_FROM_DATA(data, script) data->engines[script]
245QFontEngine *QFontPrivate::engineForScript(
int script)
const
247 QMutexLocker locker(qt_fontdatabase_mutex());
248 if (script <= QChar::Script_Latin)
249 script = QChar::Script_Common;
250 if (engineData && engineData->fontCacheId != QFontCache::instance()->id()) {
252 if (!engineData->ref.deref())
254 engineData =
nullptr;
257 QFontDatabasePrivate::load(
this, script);
261QFontEngine *QFontPrivate::engineForCharacter(
char32_t c, EngineQueryOptions opt)
const
263 const bool smallCaps = !(opt & EngineQueryOption::IgnoreSmallCapsEngine);
264 const auto script = QChar::script(c);
266 if (smallCaps && capital == QFont::SmallCaps && QChar::isLower(c))
267 engine = smallCapsFontPrivate()->engineForScript(script);
269 engine = engineForScript(script);
270 Q_ASSERT(engine !=
nullptr);
274void QFontPrivate::alterCharForCapitalization(QChar &c)
const {
276 case QFont::AllUppercase:
277 case QFont::SmallCaps:
280 case QFont::AllLowercase:
283 case QFont::MixedCase:
288QFontPrivate *QFontPrivate::smallCapsFontPrivate()
const
292 QFont font(
const_cast<QFontPrivate *>(
this));
293 qreal pointSize = font.pointSizeF();
295 font.setPointSizeF(pointSize * .7);
297 font.setPixelSize((font.pixelSize() * 7 + 5) / 10);
298 scFont = font.d.data();
305void QFontPrivate::resolve(uint mask,
const QFontPrivate *other)
307 Q_ASSERT(other !=
nullptr);
311 if ((mask & QFont::AllPropertiesResolved) == QFont::AllPropertiesResolved)
return;
314 if (!(mask & QFont::FamiliesResolved))
315 request.families = other->request.families;
317 if (! (mask & QFont::StyleNameResolved))
318 request.styleName = other->request.styleName;
320 if (! (mask & QFont::SizeResolved)) {
321 request.pointSize = other->request.pointSize;
322 request.pixelSize = other->request.pixelSize;
325 if (! (mask & QFont::StyleHintResolved))
326 request.styleHint = other->request.styleHint;
328 if (! (mask & QFont::StyleStrategyResolved))
329 request.styleStrategy = other->request.styleStrategy;
331 if (! (mask & QFont::WeightResolved))
332 request.weight = other->request.weight;
334 if (! (mask & QFont::StyleResolved))
335 request.style = other->request.style;
337 if (! (mask & QFont::FixedPitchResolved))
338 request.fixedPitch = other->request.fixedPitch;
340 if (! (mask & QFont::StretchResolved))
341 request.stretch = other->request.stretch;
343 if (! (mask & QFont::HintingPreferenceResolved))
344 request.hintingPreference = other->request.hintingPreference;
346 if (! (mask & QFont::UnderlineResolved))
347 underline = other->underline;
349 if (! (mask & QFont::OverlineResolved))
350 overline = other->overline;
352 if (! (mask & QFont::StrikeOutResolved))
353 strikeOut = other->strikeOut;
355 if (! (mask & QFont::KerningResolved))
356 kerning = other->kerning;
358 if (! (mask & QFont::LetterSpacingResolved)) {
359 letterSpacing = other->letterSpacing;
360 letterSpacingIsAbsolute = other->letterSpacingIsAbsolute;
362 if (! (mask & QFont::WordSpacingResolved))
363 wordSpacing = other->wordSpacing;
364 if (! (mask & QFont::CapitalizationResolved))
365 capital = other->capital;
367 if (!(mask & QFont::FeaturesResolved))
368 features = other->features;
370 if (!(mask & QFont::VariableAxesResolved))
371 request.variableAxisValues = other->request.variableAxisValues;
374bool QFontPrivate::hasVariableAxis(QFont::Tag tag,
float value)
const
376 return request.variableAxisValues.contains(tag) && request.variableAxisValues.value(tag) == value;
379void QFontPrivate::setVariableAxis(QFont::Tag tag,
float value)
381 request.variableAxisValues.insert(tag, value);
384void QFontPrivate::unsetVariableAxis(QFont::Tag tag)
386 request.variableAxisValues.remove(tag);
389void QFontPrivate::setFeature(QFont::Tag tag, quint32 value)
391 features.insert(tag, value);
394void QFontPrivate::unsetFeature(QFont::Tag tag)
396 features.remove(tag);
403 memset(engines, 0, QFontDatabasePrivate::ScriptCount *
sizeof(QFontEngine *));
408 Q_ASSERT(ref.loadRelaxed() == 0);
409 for (
int i = 0; i < QFontDatabasePrivate::ScriptCount; ++i) {
411 if (!engines[i]->ref.deref())
413 engines[i] =
nullptr;
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
581
582
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
611
612
613
614
615
616
617
618
619
620
621
622
623
624
627
628
629
630
631
632
635
636
637
638QFont::QFont(
const QFont &font,
const QPaintDevice *pd)
639 : resolve_mask(font.resolve_mask)
642 const int dpi = pd->logicalDpiY();
643 if (font.d->dpi != dpi) {
644 d =
new QFontPrivate(*font.d);
652
653
654QFont::QFont(QFontPrivate *data)
655 : d(data), resolve_mask(QFont::AllPropertiesResolved)
660
661
664 if (d->ref.loadRelaxed() == 1) {
665 if (d->engineData && !d->engineData->ref.deref())
666 delete d->engineData;
667 d->engineData =
nullptr;
668 if (d->scFont && d->scFont != d.data()) {
669 if (!d->scFont->ref.deref())
680
681
682
683
684
685void QFontPrivate::detachButKeepEngineData(QFont *font)
687 if (font->d->ref.loadRelaxed() == 1)
690 QFontEngineData *engineData = font->d->engineData;
692 engineData->ref.ref();
694 font->d->engineData = engineData;
698
699
700
701
703 : d(QGuiApplicationPrivate::instance() ? QGuiApplication::font().d.data() :
new QFontPrivate()), resolve_mask(0)
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729QFont::QFont(
const QString &family,
int pointSize,
int weight,
bool italic)
730 : d(
new QFontPrivate()), resolve_mask(QFont::FamiliesResolved)
732 if (pointSize <= 0) {
735 resolve_mask |= QFont::SizeResolved;
741 resolve_mask |= QFont::WeightResolved | QFont::StyleResolved;
745 resolve_mask |= QFont::StyleResolved;
747 d->request.families = splitIntoFamilies(family);
748 d->request.pointSize = qreal(pointSize);
749 d->request.pixelSize = -1;
750 d->request.weight = weight;
751 d->request.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772QFont::QFont(
const QStringList &families,
int pointSize,
int weight,
bool italic)
773 : d(
new QFontPrivate()), resolve_mask(QFont::FamiliesResolved)
778 resolve_mask |= QFont::SizeResolved;
783 resolve_mask |= QFont::WeightResolved | QFont::StyleResolved;
786 resolve_mask |= QFont::StyleResolved;
788 d->request.families = families;
789 d->request.pointSize = qreal(pointSize);
790 d->request.pixelSize = -1;
791 d->request.weight = weight;
792 d->request.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
796
797
798QFont::QFont(
const QFont &font)
799 : d(font.d), resolve_mask(font.resolve_mask)
804
805
811
812
813QFont &QFont::operator=(
const QFont &font)
816 resolve_mask = font.resolve_mask;
821
822
823
824
827
828
829
830
831
832QString QFont::family()
const
834 return d->request.families.isEmpty() ? QString() : d->request.families.constFirst();
838
839
840
841
842
843
844
845
846
847
848
849
850void QFont::setFamily(
const QString &family)
852 setFamilies(QStringList(family));
856
857
858
859
860
861
862
863
864QString QFont::styleName()
const
866 return d->request.styleName;
870
871
872
873
874
875
876
877
878
879
880
881
882void QFont::setStyleName(
const QString &styleName)
884 if ((resolve_mask & QFont::StyleNameResolved) && d->request.styleName == styleName)
889 d->request.styleName = styleName;
890 resolve_mask |= QFont::StyleNameResolved;
894
895
896
897
898
899int QFont::pointSize()
const
901 return qRound(d->request.pointSize);
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
959
960
963
964
965
966
967
968
969
970
971
972void QFont::setHintingPreference(HintingPreference hintingPreference)
974 if ((resolve_mask & QFont::HintingPreferenceResolved) && d->request.hintingPreference == hintingPreference)
979 d->request.hintingPreference = hintingPreference;
981 resolve_mask |= QFont::HintingPreferenceResolved;
985
986
987
988
989QFont::HintingPreference QFont::hintingPreference()
const
991 return QFont::HintingPreference(d->request.hintingPreference);
995
996
997
998
999
1000void QFont::setPointSize(
int pointSize)
1002 if (pointSize <= 0) {
1003 qWarning(
"QFont::setPointSize: Point size <= 0 (%d), must be greater than 0", pointSize);
1007 if ((resolve_mask & QFont::SizeResolved) && d->request.pointSize == qreal(pointSize))
1012 d->request.pointSize = qreal(pointSize);
1013 d->request.pixelSize = -1;
1015 resolve_mask |= QFont::SizeResolved;
1019
1020
1021
1022
1023
1024
1025void QFont::setPointSizeF(qreal pointSize)
1027 if (pointSize <= 0) {
1028 qWarning(
"QFont::setPointSizeF: Point size <= 0 (%f), must be greater than 0", pointSize);
1032 if ((resolve_mask & QFont::SizeResolved) && d->request.pointSize == pointSize)
1037 d->request.pointSize = pointSize;
1038 d->request.pixelSize = -1;
1040 resolve_mask |= QFont::SizeResolved;
1044
1045
1046
1047
1048
1049qreal QFont::pointSizeF()
const
1051 return d->request.pointSize;
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064void QFont::setPixelSize(
int pixelSize)
1066 if (pixelSize <= 0) {
1067 qWarning(
"QFont::setPixelSize: Pixel size <= 0 (%d)", pixelSize);
1071 if ((resolve_mask & QFont::SizeResolved) && d->request.pixelSize == qreal(pixelSize))
1076 d->request.pixelSize = pixelSize;
1077 d->request.pointSize = -1;
1079 resolve_mask |= QFont::SizeResolved;
1083
1084
1085
1086
1087
1088
1089int QFont::pixelSize()
const
1091 return d->request.pixelSize;
1095
1096
1097
1098
1099
1100
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1116
1117
1118
1119
1120QFont::Style QFont::style()
const
1122 return (QFont::Style)d->request.style;
1127
1128
1129
1130
1131void QFont::setStyle(Style style)
1133 if ((resolve_mask & QFont::StyleResolved) && d->request.style == style)
1138 d->request.style = style;
1139 resolve_mask |= QFont::StyleResolved;
1143
1144
1145
1146
1147
1148QFont::Weight QFont::weight()
const
1150 return static_cast<Weight>(d->request.weight);
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1172#if QT_DEPRECATED_SINCE(6
, 0
)
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188void QFont::setLegacyWeight(
int legacyWeight)
1190 setWeight(QFont::Weight(qt_legacyToOpenTypeWeight(legacyWeight)));
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206int QFont::legacyWeight()
const
1208 return qt_openTypeToLegacyWeight(weight());
1213
1214
1215
1216
1217
1218
1219
1220void QFont::setWeight(QFont::Weight weight)
1223 if (weightValue !=
static_cast<
int>(weight)) {
1224 qWarning() <<
"QFont::setWeight: Weight must be between 1 and 1000, attempted to set "
1225 <<
static_cast<
int>(weight);
1228 if ((resolve_mask & QFont::WeightResolved) && d->request.weight == weightValue)
1233 d->request.weight = weightValue;
1234 resolve_mask |= QFont::WeightResolved;
1238
1239
1240
1241
1242
1243
1244
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1262
1263
1264
1265
1266bool QFont::underline()
const
1268 return d->underline;
1272
1273
1274
1275
1276
1277void QFont::setUnderline(
bool enable)
1279 if ((resolve_mask & QFont::UnderlineResolved) && d->underline == enable)
1282 QFontPrivate::detachButKeepEngineData(
this);
1284 d->underline = enable;
1285 resolve_mask |= QFont::UnderlineResolved;
1289
1290
1291
1292
1293bool QFont::overline()
const
1299
1300
1301
1302
1303void QFont::setOverline(
bool enable)
1305 if ((resolve_mask & QFont::OverlineResolved) && d->overline == enable)
1308 QFontPrivate::detachButKeepEngineData(
this);
1310 d->overline = enable;
1311 resolve_mask |= QFont::OverlineResolved;
1315
1316
1317
1318
1319bool QFont::strikeOut()
const
1321 return d->strikeOut;
1325
1326
1327
1328
1329
1330void QFont::setStrikeOut(
bool enable)
1332 if ((resolve_mask & QFont::StrikeOutResolved) && d->strikeOut == enable)
1335 QFontPrivate::detachButKeepEngineData(
this);
1337 d->strikeOut = enable;
1338 resolve_mask |= QFont::StrikeOutResolved;
1342
1343
1344
1345
1346bool QFont::fixedPitch()
const
1348 return d->request.fixedPitch;
1352
1353
1354
1355
1356
1357void QFont::setFixedPitch(
bool enable)
1359 if ((resolve_mask & QFont::FixedPitchResolved) && d->request.fixedPitch == enable)
1364 d->request.fixedPitch = enable;
1365 d->request.ignorePitch =
false;
1366 resolve_mask |= QFont::FixedPitchResolved;
1370
1371
1372
1373
1374bool QFont::kerning()
const
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390void QFont::setKerning(
bool enable)
1392 if ((resolve_mask & QFont::KerningResolved) && d->kerning == enable)
1395 QFontPrivate::detachButKeepEngineData(
this);
1397 d->kerning = enable;
1398 resolve_mask |= QFont::KerningResolved;
1402
1403
1404
1405
1406
1407
1408
1409QFont::StyleStrategy QFont::styleStrategy()
const
1411 return (StyleStrategy) d->request.styleStrategy;
1415
1416
1417
1418
1419
1420
1421
1422QFont::StyleHint QFont::styleHint()
const
1424 return (StyleHint) d->request.styleHint;
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
1458
1459
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
1522
1523
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537void QFont::setStyleHint(StyleHint hint, StyleStrategy strategy)
1539 if ((resolve_mask & (QFont::StyleHintResolved | QFont::StyleStrategyResolved)) &&
1540 (StyleHint) d->request.styleHint == hint &&
1541 (StyleStrategy) d->request.styleStrategy == strategy)
1546 d->request.styleHint = hint;
1547 d->request.styleStrategy = strategy;
1548 resolve_mask |= QFont::StyleHintResolved;
1549 resolve_mask |= QFont::StyleStrategyResolved;
1554
1555
1556
1557
1558void QFont::setStyleStrategy(StyleStrategy s)
1560 if ((resolve_mask & QFont::StyleStrategyResolved) &&
1561 s == (StyleStrategy)d->request.styleStrategy)
1566 d->request.styleStrategy = s;
1567 resolve_mask |= QFont::StyleStrategyResolved;
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1592
1593
1594
1595
1596int QFont::stretch()
const
1598 return d->request.stretch;
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620void QFont::setStretch(
int factor)
1622 if (factor < 0 || factor > 4000) {
1623 qWarning(
"QFont::setStretch: Parameter '%d' out of range", factor);
1627 if ((resolve_mask & QFont::StretchResolved) &&
1628 d->request.stretch == (uint)factor)
1633 d->request.stretch = (uint)factor;
1634 resolve_mask |= QFont::StretchResolved;
1638
1639
1640
1641
1642
1643
1644
1645
1648
1649
1650
1651
1652
1653qreal QFont::letterSpacing()
const
1655 return d->letterSpacing.toReal();
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670void QFont::setLetterSpacing(SpacingType type, qreal spacing)
1672 const QFixed newSpacing = QFixed::fromReal(spacing);
1673 const bool absoluteSpacing = type == AbsoluteSpacing;
1674 if ((resolve_mask & QFont::LetterSpacingResolved) &&
1675 d->letterSpacingIsAbsolute == absoluteSpacing &&
1676 d->letterSpacing == newSpacing)
1679 QFontPrivate::detachButKeepEngineData(
this);
1681 d->letterSpacing = newSpacing;
1682 d->letterSpacingIsAbsolute = absoluteSpacing;
1683 resolve_mask |= QFont::LetterSpacingResolved;
1687
1688
1689
1690
1691
1692QFont::SpacingType QFont::letterSpacingType()
const
1694 return d->letterSpacingIsAbsolute ? AbsoluteSpacing : PercentageSpacing;
1698
1699
1700
1701
1702
1703qreal QFont::wordSpacing()
const
1705 return d->wordSpacing.toReal();
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722void QFont::setWordSpacing(qreal spacing)
1724 const QFixed newSpacing = QFixed::fromReal(spacing);
1725 if ((resolve_mask & QFont::WordSpacingResolved) &&
1726 d->wordSpacing == newSpacing)
1729 QFontPrivate::detachButKeepEngineData(
this);
1731 d->wordSpacing = newSpacing;
1732 resolve_mask |= QFont::WordSpacingResolved;
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1750
1751
1752
1753
1754
1755
1756
1757void QFont::setCapitalization(Capitalization caps)
1759 if ((resolve_mask & QFont::CapitalizationResolved) &&
1760 capitalization() == caps)
1763 QFontPrivate::detachButKeepEngineData(
this);
1766 resolve_mask |= QFont::CapitalizationResolved;
1770
1771
1772
1773
1774
1775QFont::Capitalization QFont::capitalization()
const
1777 return static_cast<QFont::Capitalization> (d->capital);
1781
1782
1783
1784
1785
1786bool QFont::exactMatch()
const
1788 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
1789 Q_ASSERT(engine !=
nullptr);
1790 return d->request.exactMatch(engine->fontDef);
1794
1795
1796
1797
1798
1799
1800
1801
1802bool QFont::operator==(
const QFont &f)
const
1805 || (f.d->request == d->request
1806 && f.d->request.pointSize == d->request.pointSize
1807 && f.d->underline == d->underline
1808 && f.d->overline == d->overline
1809 && f.d->strikeOut == d->strikeOut
1810 && f.d->kerning == d->kerning
1811 && f.d->capital == d->capital
1812 && f.d->letterSpacingIsAbsolute == d->letterSpacingIsAbsolute
1813 && f.d->letterSpacing == d->letterSpacing
1814 && f.d->wordSpacing == d->wordSpacing
1815 && f.d->features == d->features
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831bool QFont::operator<(
const QFont &f)
const
1835 if (f.d == d)
return false;
1837 const QFontDef &r1 = f.d->request;
1838 const QFontDef &r2 = d->request;
1839 if (r1.pointSize != r2.pointSize)
return r1.pointSize < r2.pointSize;
1840 if (r1.pixelSize != r2.pixelSize)
return r1.pixelSize < r2.pixelSize;
1841 if (r1.weight != r2.weight)
return r1.weight < r2.weight;
1842 if (r1.style != r2.style)
return r1.style < r2.style;
1843 if (r1.stretch != r2.stretch)
return r1.stretch < r2.stretch;
1844 if (r1.styleHint != r2.styleHint)
return r1.styleHint < r2.styleHint;
1845 if (r1.styleStrategy != r2.styleStrategy)
return r1.styleStrategy < r2.styleStrategy;
1846 if (r1.families != r2.families)
return r1.families < r2.families;
1847 if (f.d->capital != d->capital)
return f.d->capital < d->capital;
1849 if (f.d->letterSpacingIsAbsolute != d->letterSpacingIsAbsolute)
return f.d->letterSpacingIsAbsolute < d->letterSpacingIsAbsolute;
1850 if (f.d->letterSpacing != d->letterSpacing)
return f.d->letterSpacing < d->letterSpacing;
1851 if (f.d->wordSpacing != d->wordSpacing)
return f.d->wordSpacing < d->wordSpacing;
1853 int f1attrs = (f.d->underline << 3) + (f.d->overline << 2) + (f.d->strikeOut<<1) + f.d->kerning;
1854 int f2attrs = (d->underline << 3) + (d->overline << 2) + (d->strikeOut<<1) + d->kerning;
1855 if (f1attrs != f2attrs)
return f1attrs < f2attrs;
1857 if (d->features != f.d->features) {
1858 return std::lexicographical_compare(f.d->features.keyValueBegin(), f.d->features.keyValueEnd(),
1859 d->features.keyValueBegin(), d->features.keyValueEnd());
1862 return std::lexicographical_compare(r1.variableAxisValues.keyValueBegin(), r1.variableAxisValues.keyValueEnd(),
1863 r2.variableAxisValues.keyValueBegin(), r2.variableAxisValues.keyValueEnd());
1868
1869
1870
1871
1872
1873
1874
1875
1876bool QFont::operator!=(
const QFont &f)
const
1878 return !(operator==(f));
1882
1883
1884QFont::operator QVariant()
const
1886 return QVariant::fromValue(*
this);
1890
1891
1892
1893
1894
1895
1896bool QFont::isCopyOf(
const QFont & f)
const
1902
1903
1904
1905QFont QFont::resolve(
const QFont &other)
const
1907 if (resolve_mask == 0 || (resolve_mask == other.resolve_mask && *
this == other)) {
1909 o.resolve_mask = resolve_mask;
1915 font.d->resolve(resolve_mask, other.d.data());
1921
1922
1923
1926
1927
1928
1932
1933
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949QString QFont::substitute(
const QString &familyName)
1951 QFontSubst *fontSubst = globalFontSubst();
1952 Q_ASSERT(fontSubst !=
nullptr);
1953 if (fontSubst->isEmpty())
1955 QFontSubst::ConstIterator it = fontSubst->constFind(familyName.toLower());
1956 if (it != fontSubst->constEnd() && !(*it).isEmpty())
1957 return (*it).first();
1964
1965
1966
1967
1968
1969
1970
1971
1972QStringList QFont::substitutes(
const QString &familyName)
1974 QFontSubst *fontSubst = globalFontSubst();
1975 Q_ASSERT(fontSubst !=
nullptr);
1976 if (fontSubst->isEmpty())
1978 return fontSubst->value(familyName.toLower(), QStringList());
1983
1984
1985
1986
1987
1988
1989
1990
1991void QFont::insertSubstitution(
const QString &familyName,
1992 const QString &substituteName)
1994 QFontSubst *fontSubst = globalFontSubst();
1995 Q_ASSERT(fontSubst !=
nullptr);
1996 QStringList &list = (*fontSubst)[familyName.toLower()];
1997 QString s = substituteName.toLower();
1998 if (!list.contains(s))
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013void QFont::insertSubstitutions(
const QString &familyName,
2014 const QStringList &substituteNames)
2016 QFontSubst *fontSubst = globalFontSubst();
2017 Q_ASSERT(fontSubst !=
nullptr);
2018 QStringList &list = (*fontSubst)[familyName.toLower()];
2019 for (
const QString &substituteName : substituteNames) {
2020 const QString lowerSubstituteName = substituteName.toLower();
2021 if (!list.contains(lowerSubstituteName))
2022 list.append(lowerSubstituteName);
2027
2028
2029
2030
2031
2032void QFont::removeSubstitutions(
const QString &familyName)
2034 QFontSubst *fontSubst = globalFontSubst();
2035 Q_ASSERT(fontSubst !=
nullptr);
2036 fontSubst->remove(familyName.toLower());
2040
2041
2042
2043
2044QStringList QFont::substitutions()
2046 QFontSubst *fontSubst = globalFontSubst();
2047 Q_ASSERT(fontSubst !=
nullptr);
2048 QStringList ret = fontSubst->keys();
2054#ifndef QT_NO_DATASTREAM
2056
2057
2058
2061 Q_ASSERT(f !=
nullptr);
2063 if (f->request.style)
2071 if (f->request.fixedPitch)
2075 if (version >= QDataStream::Qt_4_0) {
2079 if (f->request.style == QFont::StyleOblique)
2086 Q_ASSERT(f !=
nullptr);
2088 if (f->request.ignorePitch)
2090 if (f->letterSpacingIsAbsolute)
2096
2097
2098
2101 Q_ASSERT(f !=
nullptr);
2102 f->request.style = (bits & 0x01) != 0 ? QFont::StyleItalic : QFont::StyleNormal;
2103 f->underline = (bits & 0x02) != 0;
2104 f->overline = (bits & 0x40) != 0;
2105 f->strikeOut = (bits & 0x04) != 0;
2106 f->request.fixedPitch = (bits & 0x08) != 0;
2108 if (version >= QDataStream::Qt_4_0)
2109 f->kerning = (bits & 0x10) != 0;
2110 if ((bits & 0x80) != 0)
2111 f->request.style = QFont::StyleOblique;
2116 Q_ASSERT(f !=
nullptr);
2117 f->request.ignorePitch = (bits & 0x01) != 0;
2118 f->letterSpacingIsAbsolute = (bits & 0x02) != 0;
2123
2124
2125
2126
2127
2128QString QFont::key()
const
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161QString QFont::toString()
const
2163 const QChar comma(u',');
2164 QString fontDescription = family() + comma +
2165 QString::number( pointSizeF()) + comma +
2166 QString::number( pixelSize()) + comma +
2167 QString::number((
int) styleHint()) + comma +
2168 QString::number( weight()) + comma +
2169 QString::number((
int) style()) + comma +
2170 QString::number((
int) underline()) + comma +
2171 QString::number((
int) strikeOut()) + comma +
2172 QString::number((
int)fixedPitch()) + comma +
2173 QString::number((
int)
false) + comma +
2174 QString::number((
int)capitalization()) + comma +
2175 QString::number((
int)letterSpacingType()) + comma +
2176 QString::number(letterSpacing()) + comma +
2177 QString::number(wordSpacing()) + comma +
2178 QString::number(stretch()) + comma +
2179 QString::number((
int)styleStrategy()) + comma +
2182 fontDescription += comma + QString::number(d->features.size());
2183 for (
const auto &[tag, value] : std::as_const(d->features).asKeyValueRange())
2184 fontDescription += comma + QLatin1StringView{tag.toString()} + u'=' + QString::number(value);
2186 fontDescription += comma + QString::number(d->request.variableAxisValues.size());
2187 for (
const auto &[tag, value] : std::as_const(d->request.variableAxisValues).asKeyValueRange())
2188 fontDescription += comma + QLatin1StringView{tag.toString()} + u'=' + QString::number(value);
2190 return fontDescription;
2194
2195
2196
2197
2200 return qHash(QFontPrivate::get(font)->request, seed);
2205 const int separator = view.indexOf(u'=');
2206 if (separator == -1)
2207 return std::nullopt;
2209 const std::optional<QFont::Tag> tag = QFont::Tag::fromString(view.sliced(0, separator));
2211 return std::nullopt;
2213 bool valueOk =
false;
2214 const quint32 value = view.sliced(separator + 1).toUInt(&valueOk);
2216 return std::nullopt;
2218 return std::make_pair(*tag, value);
2223 const int separator = view.indexOf(u'=');
2224 if (separator == -1)
2225 return std::nullopt;
2227 const std::optional<QFont::Tag> tag = QFont::Tag::fromString(view.sliced(0, separator));
2229 return std::nullopt;
2231 bool valueOk =
false;
2232 const float value = view.sliced(separator + 1).toFloat(&valueOk);
2234 return std::nullopt;
2236 return std::make_pair(*tag, value);
2240
2241
2242
2243
2244
2245
2246bool QFont::fromString(
const QString &descrip)
2248 const auto sr = QStringView(descrip).trimmed();
2249 const auto l = sr.split(u',');
2250 const int count = l.size();
2251 if (!count || (count > 2 && count < 10) || l.first().isEmpty()) {
2252 qWarning(
"QFont::fromString: Invalid description '%s'",
2253 descrip.isEmpty() ?
"(empty)" : descrip.toLatin1().data());
2257 setFamily(l[0].toString());
2258 if (count > 1 && l[1].toDouble() > 0.0)
2259 setPointSizeF(l[1].toDouble());
2262 if (l[2].toInt() > 0)
2263 setPixelSize(l[2].toInt());
2264 setStyleHint((StyleHint) l[3].toInt());
2266 setWeight(QFont::Weight(l[4].toInt()));
2268 setWeight(QFont::Weight(qt_legacyToOpenTypeWeight(l[4].toInt())));
2269 setStyle((QFont::Style)l[5].toInt());
2270 setUnderline(l[6].toInt());
2271 setStrikeOut(l[7].toInt());
2272 setFixedPitch(l[8].toInt());
2273 if (!d->request.fixedPitch)
2274 d->request.ignorePitch =
true;
2276 setCapitalization((Capitalization)l[10].toInt());
2277 setLetterSpacing((SpacingType)l[11].toInt(), l[12].toDouble());
2278 setWordSpacing(l[13].toDouble());
2279 setStretch(l[14].toInt());
2280 setStyleStrategy((StyleStrategy)l[15].toInt());
2284 d->request.styleName = l[10].toString();
2285 else if (count >= 17)
2286 d->request.styleName = l[16].toString();
2288 d->request.styleName.clear();
2291 clearVariableAxes();
2294 if (position >= count)
2297 const int featureCount = l[position++].toInt();
2298 if (position + featureCount > count)
2301 for (
int i = 0; i < featureCount; ++i) {
2302 if (
const auto feature = fontFeatureFromString(l[position++]))
2303 setFeature(feature->first, feature->second);
2306 if (position >= count)
2309 const int variableAxisCount = l[position++].toInt();
2310 if (position + variableAxisCount > count)
2313 for (
int i = 0; i < variableAxisCount; ++i) {
2314 if (
const auto axis = variableAxisFromString(l[position++]))
2315 setVariableAxis(axis->first, axis->second);
2323
2324
2325
2326
2327
2328
2329void QFont::initialize()
2334
2335
2336
2337
2338void QFont::cleanup()
2340 QFontCache::cleanup();
2344
2345
2346
2347void QFont::cacheStatistics()
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2382
2383
2384
2385
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2401
2402
2403
2404
2405
2408
2409
2410
2413
2414
2415
2416
2417
2418
2421
2422
2423
2424
2425
2426
2429
2430
2431
2432
2433
2434
2437
2438
2439
2440
2441
2442
2443
2446
2447
2448
2449
2450
2451
2452
2453
2454std::optional<QFont::Tag> QFont::Tag::fromString(QAnyStringView view)
noexcept
2456 if (view.size() != 4) {
2457 qWarning(
"The tag name must be exactly 4 characters long!");
2458 return std::nullopt;
2460 const QFont::Tag maybeTag = view.visit([](
auto view) {
2461 using CharType =
decltype(view.at(0));
2462 if constexpr (std::is_same_v<CharType,
char>) {
2463 const char bytes[5] = { view.at(0), view.at(1), view.at(2), view.at(3), 0 };
2466 const char bytes[5] = { view.at(0).toLatin1(), view.at(1).toLatin1(),
2467 view.at(2).toLatin1(), view.at(3).toLatin1(), 0 };
2471 return maybeTag.isValid() ? std::optional<Tag>(maybeTag) : std::nullopt;
2475
2476
2477
2478
2479
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
2513
2514
2515
2516
2517
2518
2519void QFont::setVariableAxis(Tag tag,
float value)
2521 if (tag.isValid()) {
2522 if (resolve_mask & QFont::VariableAxesResolved && d->hasVariableAxis(tag, value))
2527 d->setVariableAxis(tag, value);
2528 resolve_mask |= QFont::VariableAxesResolved;
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542void QFont::unsetVariableAxis(Tag tag)
2544 if (tag.isValid()) {
2547 d->unsetVariableAxis(tag);
2548 resolve_mask |= QFont::VariableAxesResolved;
2553
2554
2555
2556
2557
2558
2559
2560
2561QList<QFont::Tag> QFont::variableAxisTags()
const
2563 return d->request.variableAxisValues.keys();
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576float QFont::variableAxisValue(Tag tag)
const
2578 return d->request.variableAxisValues.value(tag);
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591bool QFont::isVariableAxisSet(Tag tag)
const
2593 return d->request.variableAxisValues.contains(tag);
2597
2598
2599
2600
2601
2602
2603
2604
2605void QFont::clearVariableAxes()
2607 if (d->request.variableAxisValues.isEmpty())
2611 d->request.variableAxisValues.clear();
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
2645
2646
2647
2648
2649
2650
2651void QFont::setFeature(Tag tag, quint32 value)
2653 if (tag.isValid()) {
2654 d->detachButKeepEngineData(
this);
2655 d->setFeature(tag, value);
2656 resolve_mask |= QFont::FeaturesResolved;
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675void QFont::unsetFeature(Tag tag)
2677 if (tag.isValid()) {
2678 d->detachButKeepEngineData(
this);
2679 d->unsetFeature(tag);
2680 resolve_mask |= QFont::FeaturesResolved;
2685
2686
2687
2688
2689
2690
2691
2692
2693QList<QFont::Tag> QFont::featureTags()
const
2695 return d->features.keys();
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708quint32 QFont::featureValue(Tag tag)
const
2710 return d->features.value(tag);
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723bool QFont::isFeatureSet(Tag tag)
const
2725 return d->features.contains(tag);
2729
2730
2731
2732
2733
2734
2735
2736
2737void QFont::clearFeatures()
2739 if (d->features.isEmpty())
2742 d->detachButKeepEngineData(
this);
2743 d->features.clear();
2748 QFont::StyleHint styleHint,
2749 QFontDatabasePrivate::ExtendedScript script);
2752
2753
2754
2755
2756
2757
2758
2759QString QFont::defaultFamily()
const
2761 const QStringList fallbacks = qt_fallbacksForFamily(QString(),
2763 QFont::StyleHint(d->request.styleHint),
2764 QFontDatabasePrivate::Script_Common);
2765 if (!fallbacks.isEmpty())
2766 return fallbacks.first();
2771
2772
2773
2774
2775
2776
2777
2778
2780QStringList QFont::families()
const
2782 return d->request.families;
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2802void QFont::setFamilies(
const QStringList &families)
2804 if ((resolve_mask & QFont::FamiliesResolved) && d->request.families == families)
2807 d->request.families = families;
2808 resolve_mask |= QFont::FamiliesResolved;
2813
2814
2815#ifndef QT_NO_DATASTREAM
2818
2819
2820
2821
2822
2823
2824
2825QDataStream &operator<<(QDataStream &s,
const QFont &font)
2827 if (s.version() == 1) {
2828 s << font.d->request.families.constFirst().toLatin1();
2830 s << font.d->request.families.constFirst();
2831 if (s.version() >= QDataStream::Qt_5_4)
2832 s << font.d->request.styleName;
2835 if (s.version() >= QDataStream::Qt_4_0) {
2837 double pointSize = font.d->request.pointSize;
2838 qint32 pixelSize = font.d->request.pixelSize;
2841 }
else if (s.version() <= 3) {
2842 qint16 pointSize = (qint16) (font.d->request.pointSize * 10);
2843 if (pointSize < 0) {
2844 pointSize = (qint16)QFontInfo(font).pointSize() * 10;
2848 s << (qint16) (font.d->request.pointSize * 10);
2849 s << (qint16) font.d->request.pixelSize;
2852 s << (quint8) font.d->request.styleHint;
2853 if (s.version() >= QDataStream::Qt_3_1) {
2856 if (s.version() >= QDataStream::Qt_5_4)
2857 s << (quint16) font.d->request.styleStrategy;
2859 s << (quint8) font.d->request.styleStrategy;
2862 if (s.version() < QDataStream::Qt_6_0)
2863 s << quint8(0) << quint8(qt_openTypeToLegacyWeight(font.d->request.weight));
2865 s << quint16(font.d->request.weight);
2867 s << get_font_bits(s.version(), font.d.data());
2868 if (s.version() >= QDataStream::Qt_4_3)
2869 s << (quint16)font.d->request.stretch;
2870 if (s.version() >= QDataStream::Qt_4_4)
2871 s << get_extended_font_bits(font.d.data());
2872 if (s.version() >= QDataStream::Qt_4_5) {
2873 s << font.d->letterSpacing.value();
2874 s << font.d->wordSpacing.value();
2876 if (s.version() >= QDataStream::Qt_5_4)
2877 s << (quint8)font.d->request.hintingPreference;
2878 if (s.version() >= QDataStream::Qt_5_6)
2879 s << (quint8)font.d->capital;
2880 if (s.version() >= QDataStream::Qt_5_13) {
2881 if (s.version() < QDataStream::Qt_6_0)
2882 s << font.d->request.families.mid(1);
2884 s << font.d->request.families;
2886 if (s.version() >= QDataStream::Qt_6_6)
2887 s << font.d->features;
2888 if (s.version() >= QDataStream::Qt_6_7)
2889 s << font.d->request.variableAxisValues;
2895
2896
2897
2898
2899
2900
2901
2904 font.d =
new QFontPrivate;
2905 font.resolve_mask = QFont::AllPropertiesResolved;
2907 quint8 styleHint, bits;
2908 quint16 styleStrategy = QFont::PreferDefault;
2910 if (s.version() == 1) {
2913 font.d->request.families = QStringList(QString::fromLatin1(fam));
2917 font.d->request.families = QStringList(fam);
2918 if (s.version() >= QDataStream::Qt_5_4)
2919 s >> font.d->request.styleName;
2922 if (s.version() >= QDataStream::Qt_4_0) {
2928 font.d->request.pointSize = qreal(pointSize);
2929 font.d->request.pixelSize = pixelSize;
2931 qint16 pointSize, pixelSize = -1;
2933 if (s.version() >= 4)
2935 font.d->request.pointSize = qreal(pointSize / 10.);
2936 font.d->request.pixelSize = pixelSize;
2939 if (s.version() >= QDataStream::Qt_3_1) {
2940 if (s.version() >= QDataStream::Qt_5_4) {
2943 quint8 tempStyleStrategy;
2944 s >> tempStyleStrategy;
2945 styleStrategy = tempStyleStrategy;
2949 if (s.version() < QDataStream::Qt_6_0) {
2954 font.d->request.weight = qt_legacyToOpenTypeWeight(weight);
2958 font.d->request.weight = weight;
2963 font.d->request.styleHint = styleHint;
2964 font.d->request.styleStrategy = styleStrategy;
2966 set_font_bits(s.version(), bits, font.d.data());
2968 if (s.version() >= QDataStream::Qt_4_3) {
2971 font.d->request.stretch = stretch;
2974 if (s.version() >= QDataStream::Qt_4_4) {
2975 quint8 extendedBits;
2977 set_extended_font_bits(extendedBits, font.d.data());
2979 if (s.version() >= QDataStream::Qt_4_5) {
2982 font.d->letterSpacing.setValue(value);
2984 font.d->wordSpacing.setValue(value);
2986 if (s.version() >= QDataStream::Qt_5_4) {
2989 font.d->request.hintingPreference = QFont::HintingPreference(value);
2991 if (s.version() >= QDataStream::Qt_5_6) {
2994 font.d->capital = QFont::Capitalization(value);
2996 if (s.version() >= QDataStream::Qt_5_13) {
2999 if (s.version() < QDataStream::Qt_6_0)
3000 font.d->request.families.append(value);
3002 font.d->request.families = value;
3004 if (s.version() >= QDataStream::Qt_6_6) {
3005 font.d->features.clear();
3006 s >> font.d->features;
3008 if (s.version() >= QDataStream::Qt_6_7) {
3009 font.d->request.variableAxisValues.clear();
3010 s >> font.d->request.variableAxisValues;
3016QDataStream &operator<<(QDataStream &stream, QFont::Tag tag)
3018 stream << tag.value();
3022QDataStream &
operator>>(QDataStream &stream, QFont::Tag &tag)
3026 if (
const auto maybeTag = QFont::Tag::fromValue(value))
3029 stream.setStatus(QDataStream::ReadCorruptData);
3037
3038
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
3108
3109
3110
3111
3112
3113
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131QFontInfo::QFontInfo(
const QFont &font)
3137
3138
3139QFontInfo::QFontInfo(
const QFontInfo &fi)
3145
3146
3147QFontInfo::~QFontInfo()
3152
3153
3154QFontInfo &QFontInfo::operator=(
const QFontInfo &fi)
3161
3162
3163
3164
3167
3168
3169
3170
3171QString QFontInfo::family()
const
3173 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3174 Q_ASSERT(engine !=
nullptr);
3175 return engine->fontDef.families.isEmpty() ? QString() : engine->fontDef.families.constFirst();
3179
3180
3181
3182
3183
3184
3185
3186QString QFontInfo::styleName()
const
3188 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3189 Q_ASSERT(engine !=
nullptr);
3190 return engine->fontDef.styleName;
3194
3195
3196
3197
3198int QFontInfo::pointSize()
const
3200 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3201 Q_ASSERT(engine !=
nullptr);
3202 return qRound(engine->fontDef.pointSize);
3206
3207
3208
3209
3210qreal QFontInfo::pointSizeF()
const
3212 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3213 Q_ASSERT(engine !=
nullptr);
3214 return engine->fontDef.pointSize;
3218
3219
3220
3221
3222int QFontInfo::pixelSize()
const
3224 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3225 Q_ASSERT(engine !=
nullptr);
3226 return engine->fontDef.pixelSize;
3230
3231
3232
3233
3234bool QFontInfo::italic()
const
3236 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3237 Q_ASSERT(engine !=
nullptr);
3238 return engine->fontDef.style != QFont::StyleNormal;
3242
3243
3244
3245
3246QFont::Style QFontInfo::style()
const
3248 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3249 Q_ASSERT(engine !=
nullptr);
3250 return (QFont::Style)engine->fontDef.style;
3254#if QT_DEPRECATED_SINCE(6
, 0
)
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268int QFontInfo::legacyWeight()
const
3270 return qt_openTypeToLegacyWeight(weight());
3276
3277
3278
3279
3280int QFontInfo::weight()
const
3282 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3283 Q_ASSERT(engine !=
nullptr);
3284 return engine->fontDef.weight;
3289
3290
3291
3292
3293
3294
3295
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307bool QFontInfo::underline()
const
3309 return d->underline;
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322bool QFontInfo::overline()
const
3328
3329
3330
3331
3332
3333
3334
3335bool QFontInfo::strikeOut()
const
3337 return d->strikeOut;
3341
3342
3343
3344
3345bool QFontInfo::fixedPitch()
const
3347 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3348 Q_ASSERT(engine !=
nullptr);
3350 if (!engine->fontDef.fixedPitchComputed) {
3351 QChar ch[2] = { u'i', u'm' };
3352 QGlyphLayoutArray<2> g;
3354 if (engine->stringToCMap(ch, 2, &g, &l, {}) < 0)
3357 engine->fontDef.fixedPitch = g.advances[0] == g.advances[1];
3358 engine->fontDef.fixedPitchComputed =
true;
3361 return engine->fontDef.fixedPitch;
3365
3366
3367
3368
3369
3370
3371QFont::StyleHint QFontInfo::styleHint()
const
3373 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3374 Q_ASSERT(engine !=
nullptr);
3375 return (QFont::StyleHint) engine->fontDef.styleHint;
3379
3380
3381
3382
3383
3384bool QFontInfo::exactMatch()
const
3386 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3387 Q_ASSERT(engine !=
nullptr);
3388 return d->request.exactMatch(engine->fontDef);
3392
3393
3394
3395
3396
3397
3398
3399QList<QFontVariableAxis> QFontInfo::variableAxes()
const
3401 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3402 Q_ASSERT(engine !=
nullptr);
3403 return engine->variableAxes();
3411using namespace std::chrono_literals;
3413#ifdef QFONTCACHE_DEBUG
3415static constexpr auto fast_timeout = 1s;
3416static constexpr auto slow_timeout = 5s;
3422#ifndef QFONTCACHE_MIN_COST
3423# define QFONTCACHE_MIN_COST 4
*1024
3428QFontCache *QFontCache::instance()
3430 QFontCache *&fontCache = theFontCache()->localData();
3432 fontCache =
new QFontCache;
3436void QFontCache::cleanup()
3438 QThreadStorage<QFontCache *> *cache =
nullptr;
3440 cache = theFontCache();
3441 } QT_CATCH (
const std::bad_alloc &) {
3444 if (cache && cache->hasLocalData())
3445 cache->setLocalData(
nullptr);
3448Q_CONSTINIT
static QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(0);
3450QFontCache::QFontCache()
3451 : QObject(), total_cost(0), max_cost(min_cost),
3452 current_timestamp(0), fast(
false),
3453 autoClean(QGuiApplication::instance()
3454 && (QGuiApplication::instance()->thread() == QThread::currentThread())),
3455 m_id(font_cache_id.fetchAndAddRelaxed(1) + 1)
3459QFontCache::~QFontCache()
3464void QFontCache::clear()
3467 EngineDataCache::Iterator it = engineDataCache.begin(),
3468 end = engineDataCache.end();
3470 QFontEngineData *data = it.value();
3471 for (
int i = 0; i < QFontDatabasePrivate::ScriptCount; ++i) {
3472 if (data->engines[i]) {
3473 if (!data->engines[i]->ref.deref()) {
3474 Q_ASSERT(engineCacheCount.value(data->engines[i]) == 0);
3475 delete data->engines[i];
3477 data->engines[i] =
nullptr;
3480 if (!data->ref.deref()) {
3483 FC_DEBUG(
"QFontCache::clear: engineData %p still has refcount %d",
3484 data, data->ref.loadRelaxed());
3490 engineDataCache.clear();
3493 bool mightHaveEnginesLeftForCleanup;
3495 mightHaveEnginesLeftForCleanup =
false;
3496 for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
3498 QFontEngine *engine = it.value().data;
3500 const int cacheCount = --engineCacheCount[engine];
3501 Q_ASSERT(cacheCount >= 0);
3502 if (!engine->ref.deref()) {
3503 Q_ASSERT(cacheCount == 0);
3504 mightHaveEnginesLeftForCleanup = engine->type() == QFontEngine::Multi;
3506 }
else if (cacheCount == 0) {
3507 FC_DEBUG(
"QFontCache::clear: engine %p still has refcount %d",
3508 engine, engine->ref.loadRelaxed());
3510 it.value().data =
nullptr;
3513 }
while (mightHaveEnginesLeftForCleanup);
3515 engineCache.clear();
3516 engineCacheCount.clear();
3520 max_cost = min_cost;
3524QFontEngineData *QFontCache::findEngineData(
const QFontDef &def)
const
3526 EngineDataCache::ConstIterator it = engineDataCache.constFind(def);
3527 if (it == engineDataCache.constEnd())
3534void QFontCache::insertEngineData(
const QFontDef &def, QFontEngineData *engineData)
3536#ifdef QFONTCACHE_DEBUG
3537 FC_DEBUG(
"QFontCache: inserting new engine data %p", engineData);
3538 if (engineDataCache.contains(def)) {
3539 FC_DEBUG(
" QFontCache already contains engine data %p for key=(%g %g %d %d %d)",
3540 engineDataCache.value(def), def.pointSize,
3541 def.pixelSize, def.weight, def.style, def.fixedPitch);
3544 Q_ASSERT(!engineDataCache.contains(def));
3546 engineData->ref.ref();
3551 engineDataCache.insert(def, engineData);
3552 increaseCost(
sizeof(QFontEngineData));
3555QFontEngine *QFontCache::findEngine(
const Key &key)
3557 EngineCache::Iterator it = engineCache.find(key),
3558 end = engineCache.end();
3559 if (it == end)
return nullptr;
3561 Q_ASSERT(it.value().data !=
nullptr);
3562 Q_ASSERT(key.multi == (it.value().data->type() == QFontEngine::Multi));
3565 updateHitCountAndTimeStamp(it.value());
3567 return it.value().data;
3570void QFontCache::updateHitCountAndTimeStamp(Engine &value)
3573 value.timestamp = ++current_timestamp;
3575 FC_DEBUG(
"QFontCache: found font engine\n"
3576 " %p: timestamp %4u hits %3u ref %2d/%2d, type %d",
3577 value.data, value.timestamp, value.hits,
3578 value.data->ref.loadRelaxed(), engineCacheCount.value(value.data),
3579 value.data->type());
3582void QFontCache::insertEngine(
const Key &key, QFontEngine *engine,
bool insertMulti)
3584 Q_ASSERT(engine !=
nullptr);
3585 Q_ASSERT(key.multi == (engine->type() == QFontEngine::Multi));
3587#ifdef QFONTCACHE_DEBUG
3588 FC_DEBUG(
"QFontCache: inserting new engine %p, refcount %d", engine, engine->ref.loadRelaxed());
3589 if (!insertMulti && engineCache.contains(key)) {
3590 FC_DEBUG(
" QFontCache already contains engine %p for key=(%g %g %d %d %d)",
3591 engineCache.value(key).data, key.def.pointSize,
3592 key.def.pixelSize, key.def.weight, key.def.style, key.def.fixedPitch);
3600 Engine data(engine);
3601 data.timestamp = ++current_timestamp;
3604 engineCache.insert(key, data);
3606 engineCache.replace(key, data);
3608 if (++engineCacheCount[engine] == 1)
3609 increaseCost(engine->cache_cost);
3612void QFontCache::increaseCost(uint cost)
3614 cost = (cost + 512) / 1024;
3615 cost = cost > 0 ? cost : 1;
3618 FC_DEBUG(
" COST: increased %u kb, total_cost %u kb, max_cost %u kb",
3619 cost, total_cost, max_cost);
3621 if (total_cost > max_cost) {
3622 max_cost = total_cost;
3627 if (!timer.isActive() || ! fast) {
3628 FC_DEBUG(
" TIMER: starting fast timer (%d s)",
static_cast<
int>(fast_timeout.count()));
3630 timer.start(fast_timeout,
this);
3636void QFontCache::decreaseCost(uint cost)
3638 cost = (cost + 512) / 1024;
3639 cost = cost > 0 ? cost : 1;
3640 Q_ASSERT(cost <= total_cost);
3643 FC_DEBUG(
" COST: decreased %u kb, total_cost %u kb, max_cost %u kb",
3644 cost, total_cost, max_cost);
3647void QFontCache::timerEvent(QTimerEvent *)
3649 FC_DEBUG(
"QFontCache::timerEvent: performing cache maintenance (timestamp %u)",
3652 if (total_cost <= max_cost && max_cost <= min_cost) {
3653 FC_DEBUG(
" cache redused sufficiently, stopping timer");
3663void QFontCache::decreaseCache()
3666 uint in_use_cost = 0;
3672 const uint engine_data_cost =
3673 sizeof(QFontEngineData) > 1024 ?
sizeof(QFontEngineData) : 1024;
3675 EngineDataCache::ConstIterator it = engineDataCache.constBegin(),
3676 end = engineDataCache.constEnd();
3677 for (; it != end; ++it) {
3678 FC_DEBUG(
" %p: ref %2d", it.value(),
int(it.value()->ref.loadRelaxed()));
3680 if (it.value()->ref.loadRelaxed() != 1)
3681 in_use_cost += engine_data_cost;
3688 EngineCache::ConstIterator it = engineCache.constBegin(),
3689 end = engineCache.constEnd();
3690 for (; it != end; ++it) {
3691 const auto useCount = engineCacheCount.value(it.value().data);
3692 const auto refCount = it.value().data->ref.loadRelaxed();
3693 const auto cacheCost = it.value().data->cache_cost;
3695 FC_DEBUG(
" %p: timestamp %4u hits %2u ref %2d/%2d, cost %u bytes",
3696 it.value().data, it.value().timestamp, it.value().hits,
3697 refCount, useCount, cacheCost);
3699 Q_ASSERT(useCount > 0);
3700 if (useCount > 0 && refCount > useCount)
3701 in_use_cost += cacheCost / useCount;
3705 in_use_cost += engineCache.size();
3708 in_use_cost = (in_use_cost + 512) / 1024;
3711
3712
3713
3714
3715
3716
3717
3718 uint new_max_cost = qMax(qMax(max_cost / 2, in_use_cost), min_cost);
3720 FC_DEBUG(
" after sweep, in use %u kb, total %u kb, max %u kb, new max %u kb",
3721 in_use_cost, total_cost, max_cost, new_max_cost);
3724 if (new_max_cost == max_cost) {
3726 FC_DEBUG(
" cannot shrink cache, slowing timer");
3728 if (timer.isActive()) {
3729 timer.start(slow_timeout,
this);
3734 }
else if (! fast) {
3735 FC_DEBUG(
" dropping into passing gear");
3737 timer.start(fast_timeout,
this);
3742 max_cost = new_max_cost;
3748 EngineDataCache::Iterator it = engineDataCache.begin();
3749 while (it != engineDataCache.end()) {
3750 if (it.value()->ref.loadRelaxed() == 1) {
3752 decreaseCost(
sizeof(QFontEngineData));
3753 it.value()->ref.deref();
3755 it = engineDataCache.erase(it);
3765 bool cost_decreased;
3767 cost_decreased =
false;
3769 EngineCache::Iterator it = engineCache.begin(),
3770 end = engineCache.end();
3773 uint least_popular = ~0u;
3775 EngineCache::Iterator jt = end;
3777 for ( ; it != end; ++it) {
3778 if (it.value().data->ref.loadRelaxed() != engineCacheCount.value(it.value().data))
3781 if (it.value().timestamp < oldest && it.value().hits <= least_popular) {
3782 oldest = it.value().timestamp;
3783 least_popular = it.value().hits;
3790 FC_DEBUG(
" %p: timestamp %4u hits %2u ref %2d/%2d, type %d",
3791 it.value().data, it.value().timestamp, it.value().hits,
3792 it.value().data->ref.loadRelaxed(), engineCacheCount.value(it.value().data),
3793 it.value().data->type());
3795 QFontEngine *fontEngine = it.value().data;
3797 it = engineCache.begin();
3798 while (it != engineCache.end()) {
3799 if (it.value().data == fontEngine) {
3800 fontEngine->ref.deref();
3801 it = engineCache.erase(it);
3807 Q_ASSERT(fontEngine->ref.loadRelaxed() == 0);
3808 decreaseCost(fontEngine->cache_cost);
3810 engineCacheCount.remove(fontEngine);
3812 cost_decreased =
true;
3814 }
while (cost_decreased && total_cost > max_cost);
3818#ifndef QT_NO_DEBUG_STREAM
3819QDebug operator<<(QDebug stream,
const QFont &font)
3821 QDebugStateSaver saver(stream);
3822 stream.nospace().noquote();
3825 if (stream.verbosity() == QDebug::DefaultVerbosity) {
3826 stream << font.toString() <<
")";
3830 QString fontDescription;
3831 QDebug debug(&fontDescription);
3834 const QFont defaultFont(
new QFontPrivate);
3836 for (
int property = QFont::SizeResolved; property < QFont::AllPropertiesResolved; property <<= 1) {
3837 const bool resolved = (font.resolve_mask & property) != 0;
3838 if (!resolved && stream.verbosity() == QDebug::MinimumVerbosity)
3841 #define QFONT_DEBUG_SKIP_DEFAULT(prop)
3842 if ((font.prop() == defaultFont.prop()) && stream.verbosity() == 1
)
3845 QDebugStateSaver saver(debug);
3848 case QFont::SizeResolved:
3849 if (font.pointSizeF() >= 0)
3850 debug << font.pointSizeF() <<
"pt";
3851 else if (font.pixelSize() >= 0)
3852 debug << font.pixelSize() <<
"px";
3856 case QFont::StyleHintResolved:
3858 debug.verbosity(1) << font.styleHint();
break;
3859 case QFont::StyleStrategyResolved:
3861 debug.verbosity(1) << font.styleStrategy();
break;
3862 case QFont::WeightResolved:
3863 debug.verbosity(1) << QFont::Weight(font.weight());
break;
3864 case QFont::StyleResolved:
3866 debug.verbosity(0) << font.style();
break;
3867 case QFont::UnderlineResolved:
3869 debug <<
"underline=" << font.underline();
break;
3870 case QFont::OverlineResolved:
3872 debug <<
"overline=" << font.overline();
break;
3873 case QFont::StrikeOutResolved:
3875 debug <<
"strikeOut=" << font.strikeOut();
break;
3876 case QFont::FixedPitchResolved:
3878 debug <<
"fixedPitch=" << font.fixedPitch();
break;
3879 case QFont::StretchResolved:
3881 debug.verbosity(0) << QFont::Stretch(font.stretch());
break;
3882 case QFont::KerningResolved:
3884 debug <<
"kerning=" << font.kerning();
break;
3885 case QFont::CapitalizationResolved:
3887 debug.verbosity(0) << font.capitalization();
break;
3888 case QFont::LetterSpacingResolved:
3890 debug <<
"letterSpacing=" << font.letterSpacing();
3891 debug.verbosity(0) <<
" (" << font.letterSpacingType() <<
")";
3893 case QFont::HintingPreferenceResolved:
3895 debug.verbosity(0) << font.hintingPreference();
break;
3896 case QFont::StyleNameResolved:
3898 debug <<
"styleName=" << font.styleName();
break;
3903 #undef QFONT_DEBUG_SKIP_DEFAULT
3908 if (stream.verbosity() > QDebug::MinimumVerbosity)
3909 debug.verbosity(0) <<
"resolveMask=" << QFlags<QFont::ResolveProperties>(font.resolve_mask);
3911 fontDescription.chop(2);
3913 stream << fontDescription <<
')';
3918QDebug operator<<(QDebug debug, QFont::Tag tag)
3920 QDebugStateSaver saver(debug);
3921 debug.noquote() << tag.toString();
3928#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