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)
116 if (QCoreApplication::instance()->testAttribute(Qt::AA_Use96Dpi))
122 if (
const QScreen *screen = QGuiApplication::primaryScreen())
123 return qRound(screen->logicalDotsPerInchX());
131 if (QCoreApplication::instance()->testAttribute(Qt::AA_Use96Dpi))
137 if (
const QScreen *screen = QGuiApplication::primaryScreen())
138 return qRound(screen->logicalDotsPerInchY());
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 QFontSubst::ConstIterator it = fontSubst->constFind(familyName.toLower());
1954 if (it != fontSubst->constEnd() && !(*it).isEmpty())
1955 return (*it).first();
1962
1963
1964
1965
1966
1967
1968
1969
1970QStringList QFont::substitutes(
const QString &familyName)
1972 QFontSubst *fontSubst = globalFontSubst();
1973 Q_ASSERT(fontSubst !=
nullptr);
1974 return fontSubst->value(familyName.toLower(), QStringList());
1979
1980
1981
1982
1983
1984
1985
1986
1987void QFont::insertSubstitution(
const QString &familyName,
1988 const QString &substituteName)
1990 QFontSubst *fontSubst = globalFontSubst();
1991 Q_ASSERT(fontSubst !=
nullptr);
1992 QStringList &list = (*fontSubst)[familyName.toLower()];
1993 QString s = substituteName.toLower();
1994 if (!list.contains(s))
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009void QFont::insertSubstitutions(
const QString &familyName,
2010 const QStringList &substituteNames)
2012 QFontSubst *fontSubst = globalFontSubst();
2013 Q_ASSERT(fontSubst !=
nullptr);
2014 QStringList &list = (*fontSubst)[familyName.toLower()];
2015 for (
const QString &substituteName : substituteNames) {
2016 const QString lowerSubstituteName = substituteName.toLower();
2017 if (!list.contains(lowerSubstituteName))
2018 list.append(lowerSubstituteName);
2023
2024
2025
2026
2027
2028void QFont::removeSubstitutions(
const QString &familyName)
2030 QFontSubst *fontSubst = globalFontSubst();
2031 Q_ASSERT(fontSubst !=
nullptr);
2032 fontSubst->remove(familyName.toLower());
2036
2037
2038
2039
2040QStringList QFont::substitutions()
2042 QFontSubst *fontSubst = globalFontSubst();
2043 Q_ASSERT(fontSubst !=
nullptr);
2044 QStringList ret = fontSubst->keys();
2050#ifndef QT_NO_DATASTREAM
2052
2053
2054
2057 Q_ASSERT(f !=
nullptr);
2059 if (f->request.style)
2067 if (f->request.fixedPitch)
2071 if (version >= QDataStream::Qt_4_0) {
2075 if (f->request.style == QFont::StyleOblique)
2082 Q_ASSERT(f !=
nullptr);
2084 if (f->request.ignorePitch)
2086 if (f->letterSpacingIsAbsolute)
2092
2093
2094
2097 Q_ASSERT(f !=
nullptr);
2098 f->request.style = (bits & 0x01) != 0 ? QFont::StyleItalic : QFont::StyleNormal;
2099 f->underline = (bits & 0x02) != 0;
2100 f->overline = (bits & 0x40) != 0;
2101 f->strikeOut = (bits & 0x04) != 0;
2102 f->request.fixedPitch = (bits & 0x08) != 0;
2104 if (version >= QDataStream::Qt_4_0)
2105 f->kerning = (bits & 0x10) != 0;
2106 if ((bits & 0x80) != 0)
2107 f->request.style = QFont::StyleOblique;
2112 Q_ASSERT(f !=
nullptr);
2113 f->request.ignorePitch = (bits & 0x01) != 0;
2114 f->letterSpacingIsAbsolute = (bits & 0x02) != 0;
2119
2120
2121
2122
2123
2124QString QFont::key()
const
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
2155
2156
2157QString QFont::toString()
const
2159 const QChar comma(u',');
2160 QString fontDescription = family() + comma +
2161 QString::number( pointSizeF()) + comma +
2162 QString::number( pixelSize()) + comma +
2163 QString::number((
int) styleHint()) + comma +
2164 QString::number( weight()) + comma +
2165 QString::number((
int) style()) + comma +
2166 QString::number((
int) underline()) + comma +
2167 QString::number((
int) strikeOut()) + comma +
2168 QString::number((
int)fixedPitch()) + comma +
2169 QString::number((
int)
false) + comma +
2170 QString::number((
int)capitalization()) + comma +
2171 QString::number((
int)letterSpacingType()) + comma +
2172 QString::number(letterSpacing()) + comma +
2173 QString::number(wordSpacing()) + comma +
2174 QString::number(stretch()) + comma +
2175 QString::number((
int)styleStrategy()) + comma +
2178 fontDescription += comma + QString::number(d->features.size());
2179 for (
const auto &[tag, value] : std::as_const(d->features).asKeyValueRange())
2180 fontDescription += comma + QLatin1StringView{tag.toString()} + u'=' + QString::number(value);
2182 fontDescription += comma + QString::number(d->request.variableAxisValues.size());
2183 for (
const auto &[tag, value] : std::as_const(d->request.variableAxisValues).asKeyValueRange())
2184 fontDescription += comma + QLatin1StringView{tag.toString()} + u'=' + QString::number(value);
2186 return fontDescription;
2190
2191
2192
2193
2196 return qHash(QFontPrivate::get(font)->request, seed);
2201 const int separator = view.indexOf(u'=');
2202 if (separator == -1)
2203 return std::nullopt;
2205 const std::optional<QFont::Tag> tag = QFont::Tag::fromString(view.sliced(0, separator));
2207 return std::nullopt;
2209 bool valueOk =
false;
2210 const quint32 value = view.sliced(separator + 1).toUInt(&valueOk);
2212 return std::nullopt;
2214 return std::make_pair(*tag, value);
2219 const int separator = view.indexOf(u'=');
2220 if (separator == -1)
2221 return std::nullopt;
2223 const std::optional<QFont::Tag> tag = QFont::Tag::fromString(view.sliced(0, separator));
2225 return std::nullopt;
2227 bool valueOk =
false;
2228 const float value = view.sliced(separator + 1).toFloat(&valueOk);
2230 return std::nullopt;
2232 return std::make_pair(*tag, value);
2236
2237
2238
2239
2240
2241
2242bool QFont::fromString(
const QString &descrip)
2244 const auto sr = QStringView(descrip).trimmed();
2245 const auto l = sr.split(u',');
2246 const int count = l.size();
2247 if (!count || (count > 2 && count < 10) || l.first().isEmpty()) {
2248 qWarning(
"QFont::fromString: Invalid description '%s'",
2249 descrip.isEmpty() ?
"(empty)" : descrip.toLatin1().data());
2253 setFamily(l[0].toString());
2254 if (count > 1 && l[1].toDouble() > 0.0)
2255 setPointSizeF(l[1].toDouble());
2258 if (l[2].toInt() > 0)
2259 setPixelSize(l[2].toInt());
2260 setStyleHint((StyleHint) l[3].toInt());
2262 setWeight(QFont::Weight(l[4].toInt()));
2264 setWeight(QFont::Weight(qt_legacyToOpenTypeWeight(l[4].toInt())));
2265 setStyle((QFont::Style)l[5].toInt());
2266 setUnderline(l[6].toInt());
2267 setStrikeOut(l[7].toInt());
2268 setFixedPitch(l[8].toInt());
2269 if (!d->request.fixedPitch)
2270 d->request.ignorePitch =
true;
2272 setCapitalization((Capitalization)l[10].toInt());
2273 setLetterSpacing((SpacingType)l[11].toInt(), l[12].toDouble());
2274 setWordSpacing(l[13].toDouble());
2275 setStretch(l[14].toInt());
2276 setStyleStrategy((StyleStrategy)l[15].toInt());
2280 d->request.styleName = l[10].toString();
2281 else if (count >= 17)
2282 d->request.styleName = l[16].toString();
2284 d->request.styleName.clear();
2287 clearVariableAxes();
2290 if (position >= count)
2293 const int featureCount = l[position++].toInt();
2294 if (position + featureCount > count)
2297 for (
int i = 0; i < featureCount; ++i) {
2298 if (
const auto feature = fontFeatureFromString(l[position++]))
2299 setFeature(feature->first, feature->second);
2302 if (position >= count)
2305 const int variableAxisCount = l[position++].toInt();
2306 if (position + variableAxisCount > count)
2309 for (
int i = 0; i < variableAxisCount; ++i) {
2310 if (
const auto axis = variableAxisFromString(l[position++]))
2311 setVariableAxis(axis->first, axis->second);
2319
2320
2321
2322
2323
2324
2325void QFont::initialize()
2330
2331
2332
2333
2334void QFont::cleanup()
2336 QFontCache::cleanup();
2340
2341
2342
2343void QFont::cacheStatistics()
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
2374
2375
2378
2379
2380
2381
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2397
2398
2399
2400
2401
2404
2405
2406
2409
2410
2411
2412
2413
2414
2417
2418
2419
2420
2421
2422
2425
2426
2427
2428
2429
2430
2433
2434
2435
2436
2437
2438
2439
2442
2443
2444
2445
2446
2447
2448
2449
2450std::optional<QFont::Tag> QFont::Tag::fromString(QAnyStringView view)
noexcept
2452 if (view.size() != 4) {
2453 qWarning(
"The tag name must be exactly 4 characters long!");
2454 return std::nullopt;
2456 const QFont::Tag maybeTag = view.visit([](
auto view) {
2457 using CharType =
decltype(view.at(0));
2458 if constexpr (std::is_same_v<CharType,
char>) {
2459 const char bytes[5] = { view.at(0), view.at(1), view.at(2), view.at(3), 0 };
2462 const char bytes[5] = { view.at(0).toLatin1(), view.at(1).toLatin1(),
2463 view.at(2).toLatin1(), view.at(3).toLatin1(), 0 };
2467 return maybeTag.isValid() ? std::optional<Tag>(maybeTag) : std::nullopt;
2471
2472
2473
2474
2475
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
2513
2514
2515void QFont::setVariableAxis(Tag tag,
float value)
2517 if (tag.isValid()) {
2518 if (resolve_mask & QFont::VariableAxesResolved && d->hasVariableAxis(tag, value))
2523 d->setVariableAxis(tag, value);
2524 resolve_mask |= QFont::VariableAxesResolved;
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538void QFont::unsetVariableAxis(Tag tag)
2540 if (tag.isValid()) {
2543 d->unsetVariableAxis(tag);
2544 resolve_mask |= QFont::VariableAxesResolved;
2549
2550
2551
2552
2553
2554
2555
2556
2557QList<QFont::Tag> QFont::variableAxisTags()
const
2559 return d->request.variableAxisValues.keys();
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572float QFont::variableAxisValue(Tag tag)
const
2574 return d->request.variableAxisValues.value(tag);
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587bool QFont::isVariableAxisSet(Tag tag)
const
2589 return d->request.variableAxisValues.contains(tag);
2593
2594
2595
2596
2597
2598
2599
2600
2601void QFont::clearVariableAxes()
2603 if (d->request.variableAxisValues.isEmpty())
2607 d->request.variableAxisValues.clear();
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
2645
2646
2647void QFont::setFeature(Tag tag, quint32 value)
2649 if (tag.isValid()) {
2650 d->detachButKeepEngineData(
this);
2651 d->setFeature(tag, value);
2652 resolve_mask |= QFont::FeaturesResolved;
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671void QFont::unsetFeature(Tag tag)
2673 if (tag.isValid()) {
2674 d->detachButKeepEngineData(
this);
2675 d->unsetFeature(tag);
2676 resolve_mask |= QFont::FeaturesResolved;
2681
2682
2683
2684
2685
2686
2687
2688
2689QList<QFont::Tag> QFont::featureTags()
const
2691 return d->features.keys();
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704quint32 QFont::featureValue(Tag tag)
const
2706 return d->features.value(tag);
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719bool QFont::isFeatureSet(Tag tag)
const
2721 return d->features.contains(tag);
2725
2726
2727
2728
2729
2730
2731
2732
2733void QFont::clearFeatures()
2735 if (d->features.isEmpty())
2738 d->detachButKeepEngineData(
this);
2739 d->features.clear();
2744 QFont::StyleHint styleHint,
2745 QFontDatabasePrivate::ExtendedScript script);
2748
2749
2750
2751
2752
2753
2754
2755QString QFont::defaultFamily()
const
2757 const QStringList fallbacks = qt_fallbacksForFamily(QString(),
2759 QFont::StyleHint(d->request.styleHint),
2760 QFontDatabasePrivate::Script_Common);
2761 if (!fallbacks.isEmpty())
2762 return fallbacks.first();
2767
2768
2769
2770
2771
2772
2773
2774
2776QStringList QFont::families()
const
2778 return d->request.families;
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2798void QFont::setFamilies(
const QStringList &families)
2800 if ((resolve_mask & QFont::FamiliesResolved) && d->request.families == families)
2803 d->request.families = families;
2804 resolve_mask |= QFont::FamiliesResolved;
2809
2810
2811#ifndef QT_NO_DATASTREAM
2814
2815
2816
2817
2818
2819
2820
2821QDataStream &operator<<(QDataStream &s,
const QFont &font)
2823 if (s.version() == 1) {
2824 s << font.d->request.families.constFirst().toLatin1();
2826 s << font.d->request.families.constFirst();
2827 if (s.version() >= QDataStream::Qt_5_4)
2828 s << font.d->request.styleName;
2831 if (s.version() >= QDataStream::Qt_4_0) {
2833 double pointSize = font.d->request.pointSize;
2834 qint32 pixelSize = font.d->request.pixelSize;
2837 }
else if (s.version() <= 3) {
2838 qint16 pointSize = (qint16) (font.d->request.pointSize * 10);
2839 if (pointSize < 0) {
2840 pointSize = (qint16)QFontInfo(font).pointSize() * 10;
2844 s << (qint16) (font.d->request.pointSize * 10);
2845 s << (qint16) font.d->request.pixelSize;
2848 s << (quint8) font.d->request.styleHint;
2849 if (s.version() >= QDataStream::Qt_3_1) {
2852 if (s.version() >= QDataStream::Qt_5_4)
2853 s << (quint16) font.d->request.styleStrategy;
2855 s << (quint8) font.d->request.styleStrategy;
2858 if (s.version() < QDataStream::Qt_6_0)
2859 s << quint8(0) << quint8(qt_openTypeToLegacyWeight(font.d->request.weight));
2861 s << quint16(font.d->request.weight);
2863 s << get_font_bits(s.version(), font.d.data());
2864 if (s.version() >= QDataStream::Qt_4_3)
2865 s << (quint16)font.d->request.stretch;
2866 if (s.version() >= QDataStream::Qt_4_4)
2867 s << get_extended_font_bits(font.d.data());
2868 if (s.version() >= QDataStream::Qt_4_5) {
2869 s << font.d->letterSpacing.value();
2870 s << font.d->wordSpacing.value();
2872 if (s.version() >= QDataStream::Qt_5_4)
2873 s << (quint8)font.d->request.hintingPreference;
2874 if (s.version() >= QDataStream::Qt_5_6)
2875 s << (quint8)font.d->capital;
2876 if (s.version() >= QDataStream::Qt_5_13) {
2877 if (s.version() < QDataStream::Qt_6_0)
2878 s << font.d->request.families.mid(1);
2880 s << font.d->request.families;
2882 if (s.version() >= QDataStream::Qt_6_6)
2883 s << font.d->features;
2884 if (s.version() >= QDataStream::Qt_6_7)
2885 s << font.d->request.variableAxisValues;
2891
2892
2893
2894
2895
2896
2897
2900 font.d =
new QFontPrivate;
2901 font.resolve_mask = QFont::AllPropertiesResolved;
2903 quint8 styleHint, bits;
2904 quint16 styleStrategy = QFont::PreferDefault;
2906 if (s.version() == 1) {
2909 font.d->request.families = QStringList(QString::fromLatin1(fam));
2913 font.d->request.families = QStringList(fam);
2914 if (s.version() >= QDataStream::Qt_5_4)
2915 s >> font.d->request.styleName;
2918 if (s.version() >= QDataStream::Qt_4_0) {
2924 font.d->request.pointSize = qreal(pointSize);
2925 font.d->request.pixelSize = pixelSize;
2927 qint16 pointSize, pixelSize = -1;
2929 if (s.version() >= 4)
2931 font.d->request.pointSize = qreal(pointSize / 10.);
2932 font.d->request.pixelSize = pixelSize;
2935 if (s.version() >= QDataStream::Qt_3_1) {
2936 if (s.version() >= QDataStream::Qt_5_4) {
2939 quint8 tempStyleStrategy;
2940 s >> tempStyleStrategy;
2941 styleStrategy = tempStyleStrategy;
2945 if (s.version() < QDataStream::Qt_6_0) {
2950 font.d->request.weight = qt_legacyToOpenTypeWeight(weight);
2954 font.d->request.weight = weight;
2959 font.d->request.styleHint = styleHint;
2960 font.d->request.styleStrategy = styleStrategy;
2962 set_font_bits(s.version(), bits, font.d.data());
2964 if (s.version() >= QDataStream::Qt_4_3) {
2967 font.d->request.stretch = stretch;
2970 if (s.version() >= QDataStream::Qt_4_4) {
2971 quint8 extendedBits;
2973 set_extended_font_bits(extendedBits, font.d.data());
2975 if (s.version() >= QDataStream::Qt_4_5) {
2978 font.d->letterSpacing.setValue(value);
2980 font.d->wordSpacing.setValue(value);
2982 if (s.version() >= QDataStream::Qt_5_4) {
2985 font.d->request.hintingPreference = QFont::HintingPreference(value);
2987 if (s.version() >= QDataStream::Qt_5_6) {
2990 font.d->capital = QFont::Capitalization(value);
2992 if (s.version() >= QDataStream::Qt_5_13) {
2995 if (s.version() < QDataStream::Qt_6_0)
2996 font.d->request.families.append(value);
2998 font.d->request.families = value;
3000 if (s.version() >= QDataStream::Qt_6_6) {
3001 font.d->features.clear();
3002 s >> font.d->features;
3004 if (s.version() >= QDataStream::Qt_6_7) {
3005 font.d->request.variableAxisValues.clear();
3006 s >> font.d->request.variableAxisValues;
3012QDataStream &operator<<(QDataStream &stream, QFont::Tag tag)
3014 stream << tag.value();
3018QDataStream &
operator>>(QDataStream &stream, QFont::Tag &tag)
3022 if (
const auto maybeTag = QFont::Tag::fromValue(value))
3025 stream.setStatus(QDataStream::ReadCorruptData);
3033
3034
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
3108
3109
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127QFontInfo::QFontInfo(
const QFont &font)
3133
3134
3135QFontInfo::QFontInfo(
const QFontInfo &fi)
3141
3142
3143QFontInfo::~QFontInfo()
3148
3149
3150QFontInfo &QFontInfo::operator=(
const QFontInfo &fi)
3157
3158
3159
3160
3163
3164
3165
3166
3167QString QFontInfo::family()
const
3169 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3170 Q_ASSERT(engine !=
nullptr);
3171 return engine->fontDef.families.isEmpty() ? QString() : engine->fontDef.families.constFirst();
3175
3176
3177
3178
3179
3180
3181
3182QString QFontInfo::styleName()
const
3184 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3185 Q_ASSERT(engine !=
nullptr);
3186 return engine->fontDef.styleName;
3190
3191
3192
3193
3194int QFontInfo::pointSize()
const
3196 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3197 Q_ASSERT(engine !=
nullptr);
3198 return qRound(engine->fontDef.pointSize);
3202
3203
3204
3205
3206qreal QFontInfo::pointSizeF()
const
3208 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3209 Q_ASSERT(engine !=
nullptr);
3210 return engine->fontDef.pointSize;
3214
3215
3216
3217
3218int QFontInfo::pixelSize()
const
3220 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3221 Q_ASSERT(engine !=
nullptr);
3222 return engine->fontDef.pixelSize;
3226
3227
3228
3229
3230bool QFontInfo::italic()
const
3232 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3233 Q_ASSERT(engine !=
nullptr);
3234 return engine->fontDef.style != QFont::StyleNormal;
3238
3239
3240
3241
3242QFont::Style QFontInfo::style()
const
3244 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3245 Q_ASSERT(engine !=
nullptr);
3246 return (QFont::Style)engine->fontDef.style;
3250#if QT_DEPRECATED_SINCE(6
, 0
)
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264int QFontInfo::legacyWeight()
const
3266 return qt_openTypeToLegacyWeight(weight());
3272
3273
3274
3275
3276int QFontInfo::weight()
const
3278 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3279 Q_ASSERT(engine !=
nullptr);
3280 return engine->fontDef.weight;
3285
3286
3287
3288
3289
3290
3291
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303bool QFontInfo::underline()
const
3305 return d->underline;
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318bool QFontInfo::overline()
const
3324
3325
3326
3327
3328
3329
3330
3331bool QFontInfo::strikeOut()
const
3333 return d->strikeOut;
3337
3338
3339
3340
3341bool QFontInfo::fixedPitch()
const
3343 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3344 Q_ASSERT(engine !=
nullptr);
3346 if (!engine->fontDef.fixedPitchComputed) {
3347 QChar ch[2] = { u'i', u'm' };
3348 QGlyphLayoutArray<2> g;
3350 if (engine->stringToCMap(ch, 2, &g, &l, {}) < 0)
3353 engine->fontDef.fixedPitch = g.advances[0] == g.advances[1];
3354 engine->fontDef.fixedPitchComputed =
true;
3357 return engine->fontDef.fixedPitch;
3361
3362
3363
3364
3365
3366
3367QFont::StyleHint QFontInfo::styleHint()
const
3369 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3370 Q_ASSERT(engine !=
nullptr);
3371 return (QFont::StyleHint) engine->fontDef.styleHint;
3375
3376
3377
3378
3379
3380bool QFontInfo::exactMatch()
const
3382 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3383 Q_ASSERT(engine !=
nullptr);
3384 return d->request.exactMatch(engine->fontDef);
3388
3389
3390
3391
3392
3393
3394
3395QList<QFontVariableAxis> QFontInfo::variableAxes()
const
3397 QFontEngine *engine = d->engineForScript(QChar::Script_Common);
3398 Q_ASSERT(engine !=
nullptr);
3399 return engine->variableAxes();
3407using namespace std::chrono_literals;
3409#ifdef QFONTCACHE_DEBUG
3411static constexpr auto fast_timeout = 1s;
3412static constexpr auto slow_timeout = 5s;
3418#ifndef QFONTCACHE_MIN_COST
3419# define QFONTCACHE_MIN_COST 4
*1024
3424QFontCache *QFontCache::instance()
3426 QFontCache *&fontCache = theFontCache()->localData();
3428 fontCache =
new QFontCache;
3432void QFontCache::cleanup()
3434 QThreadStorage<QFontCache *> *cache =
nullptr;
3436 cache = theFontCache();
3437 } QT_CATCH (
const std::bad_alloc &) {
3440 if (cache && cache->hasLocalData())
3441 cache->setLocalData(
nullptr);
3444Q_CONSTINIT
static QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(0);
3446QFontCache::QFontCache()
3447 : QObject(), total_cost(0), max_cost(min_cost),
3448 current_timestamp(0), fast(
false),
3449 autoClean(QGuiApplication::instance()
3450 && (QGuiApplication::instance()->thread() == QThread::currentThread())),
3451 m_id(font_cache_id.fetchAndAddRelaxed(1) + 1)
3455QFontCache::~QFontCache()
3460void QFontCache::clear()
3463 EngineDataCache::Iterator it = engineDataCache.begin(),
3464 end = engineDataCache.end();
3466 QFontEngineData *data = it.value();
3467 for (
int i = 0; i < QFontDatabasePrivate::ScriptCount; ++i) {
3468 if (data->engines[i]) {
3469 if (!data->engines[i]->ref.deref()) {
3470 Q_ASSERT(engineCacheCount.value(data->engines[i]) == 0);
3471 delete data->engines[i];
3473 data->engines[i] =
nullptr;
3476 if (!data->ref.deref()) {
3479 FC_DEBUG(
"QFontCache::clear: engineData %p still has refcount %d",
3480 data, data->ref.loadRelaxed());
3486 engineDataCache.clear();
3489 bool mightHaveEnginesLeftForCleanup;
3491 mightHaveEnginesLeftForCleanup =
false;
3492 for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
3494 QFontEngine *engine = it.value().data;
3496 const int cacheCount = --engineCacheCount[engine];
3497 Q_ASSERT(cacheCount >= 0);
3498 if (!engine->ref.deref()) {
3499 Q_ASSERT(cacheCount == 0);
3500 mightHaveEnginesLeftForCleanup = engine->type() == QFontEngine::Multi;
3502 }
else if (cacheCount == 0) {
3503 FC_DEBUG(
"QFontCache::clear: engine %p still has refcount %d",
3504 engine, engine->ref.loadRelaxed());
3506 it.value().data =
nullptr;
3509 }
while (mightHaveEnginesLeftForCleanup);
3511 engineCache.clear();
3512 engineCacheCount.clear();
3516 max_cost = min_cost;
3520QFontEngineData *QFontCache::findEngineData(
const QFontDef &def)
const
3522 EngineDataCache::ConstIterator it = engineDataCache.constFind(def);
3523 if (it == engineDataCache.constEnd())
3530void QFontCache::insertEngineData(
const QFontDef &def, QFontEngineData *engineData)
3532#ifdef QFONTCACHE_DEBUG
3533 FC_DEBUG(
"QFontCache: inserting new engine data %p", engineData);
3534 if (engineDataCache.contains(def)) {
3535 FC_DEBUG(
" QFontCache already contains engine data %p for key=(%g %g %d %d %d)",
3536 engineDataCache.value(def), def.pointSize,
3537 def.pixelSize, def.weight, def.style, def.fixedPitch);
3540 Q_ASSERT(!engineDataCache.contains(def));
3542 engineData->ref.ref();
3547 engineDataCache.insert(def, engineData);
3548 increaseCost(
sizeof(QFontEngineData));
3551QFontEngine *QFontCache::findEngine(
const Key &key)
3553 EngineCache::Iterator it = engineCache.find(key),
3554 end = engineCache.end();
3555 if (it == end)
return nullptr;
3557 Q_ASSERT(it.value().data !=
nullptr);
3558 Q_ASSERT(key.multi == (it.value().data->type() == QFontEngine::Multi));
3561 updateHitCountAndTimeStamp(it.value());
3563 return it.value().data;
3566void QFontCache::updateHitCountAndTimeStamp(Engine &value)
3569 value.timestamp = ++current_timestamp;
3571 FC_DEBUG(
"QFontCache: found font engine\n"
3572 " %p: timestamp %4u hits %3u ref %2d/%2d, type %d",
3573 value.data, value.timestamp, value.hits,
3574 value.data->ref.loadRelaxed(), engineCacheCount.value(value.data),
3575 value.data->type());
3578void QFontCache::insertEngine(
const Key &key, QFontEngine *engine,
bool insertMulti)
3580 Q_ASSERT(engine !=
nullptr);
3581 Q_ASSERT(key.multi == (engine->type() == QFontEngine::Multi));
3583#ifdef QFONTCACHE_DEBUG
3584 FC_DEBUG(
"QFontCache: inserting new engine %p, refcount %d", engine, engine->ref.loadRelaxed());
3585 if (!insertMulti && engineCache.contains(key)) {
3586 FC_DEBUG(
" QFontCache already contains engine %p for key=(%g %g %d %d %d)",
3587 engineCache.value(key).data, key.def.pointSize,
3588 key.def.pixelSize, key.def.weight, key.def.style, key.def.fixedPitch);
3596 Engine data(engine);
3597 data.timestamp = ++current_timestamp;
3600 engineCache.insert(key, data);
3602 engineCache.replace(key, data);
3604 if (++engineCacheCount[engine] == 1)
3605 increaseCost(engine->cache_cost);
3608void QFontCache::increaseCost(uint cost)
3610 cost = (cost + 512) / 1024;
3611 cost = cost > 0 ? cost : 1;
3614 FC_DEBUG(
" COST: increased %u kb, total_cost %u kb, max_cost %u kb",
3615 cost, total_cost, max_cost);
3617 if (total_cost > max_cost) {
3618 max_cost = total_cost;
3623 if (!timer.isActive() || ! fast) {
3624 FC_DEBUG(
" TIMER: starting fast timer (%d s)",
static_cast<
int>(fast_timeout.count()));
3626 timer.start(fast_timeout,
this);
3632void QFontCache::decreaseCost(uint cost)
3634 cost = (cost + 512) / 1024;
3635 cost = cost > 0 ? cost : 1;
3636 Q_ASSERT(cost <= total_cost);
3639 FC_DEBUG(
" COST: decreased %u kb, total_cost %u kb, max_cost %u kb",
3640 cost, total_cost, max_cost);
3643void QFontCache::timerEvent(QTimerEvent *)
3645 FC_DEBUG(
"QFontCache::timerEvent: performing cache maintenance (timestamp %u)",
3648 if (total_cost <= max_cost && max_cost <= min_cost) {
3649 FC_DEBUG(
" cache redused sufficiently, stopping timer");
3659void QFontCache::decreaseCache()
3662 uint in_use_cost = 0;
3668 const uint engine_data_cost =
3669 sizeof(QFontEngineData) > 1024 ?
sizeof(QFontEngineData) : 1024;
3671 EngineDataCache::ConstIterator it = engineDataCache.constBegin(),
3672 end = engineDataCache.constEnd();
3673 for (; it != end; ++it) {
3674 FC_DEBUG(
" %p: ref %2d", it.value(),
int(it.value()->ref.loadRelaxed()));
3676 if (it.value()->ref.loadRelaxed() != 1)
3677 in_use_cost += engine_data_cost;
3684 EngineCache::ConstIterator it = engineCache.constBegin(),
3685 end = engineCache.constEnd();
3686 for (; it != end; ++it) {
3687 const auto useCount = engineCacheCount.value(it.value().data);
3688 const auto refCount = it.value().data->ref.loadRelaxed();
3689 const auto cacheCost = it.value().data->cache_cost;
3691 FC_DEBUG(
" %p: timestamp %4u hits %2u ref %2d/%2d, cost %u bytes",
3692 it.value().data, it.value().timestamp, it.value().hits,
3693 refCount, useCount, cacheCost);
3695 Q_ASSERT(useCount > 0);
3696 if (useCount > 0 && refCount > useCount)
3697 in_use_cost += cacheCost / useCount;
3701 in_use_cost += engineCache.size();
3704 in_use_cost = (in_use_cost + 512) / 1024;
3707
3708
3709
3710
3711
3712
3713
3714 uint new_max_cost = qMax(qMax(max_cost / 2, in_use_cost), min_cost);
3716 FC_DEBUG(
" after sweep, in use %u kb, total %u kb, max %u kb, new max %u kb",
3717 in_use_cost, total_cost, max_cost, new_max_cost);
3720 if (new_max_cost == max_cost) {
3722 FC_DEBUG(
" cannot shrink cache, slowing timer");
3724 if (timer.isActive()) {
3725 timer.start(slow_timeout,
this);
3730 }
else if (! fast) {
3731 FC_DEBUG(
" dropping into passing gear");
3733 timer.start(fast_timeout,
this);
3738 max_cost = new_max_cost;
3744 EngineDataCache::Iterator it = engineDataCache.begin();
3745 while (it != engineDataCache.end()) {
3746 if (it.value()->ref.loadRelaxed() == 1) {
3748 decreaseCost(
sizeof(QFontEngineData));
3749 it.value()->ref.deref();
3751 it = engineDataCache.erase(it);
3761 bool cost_decreased;
3763 cost_decreased =
false;
3765 EngineCache::Iterator it = engineCache.begin(),
3766 end = engineCache.end();
3769 uint least_popular = ~0u;
3771 EngineCache::Iterator jt = end;
3773 for ( ; it != end; ++it) {
3774 if (it.value().data->ref.loadRelaxed() != engineCacheCount.value(it.value().data))
3777 if (it.value().timestamp < oldest && it.value().hits <= least_popular) {
3778 oldest = it.value().timestamp;
3779 least_popular = it.value().hits;
3786 FC_DEBUG(
" %p: timestamp %4u hits %2u ref %2d/%2d, type %d",
3787 it.value().data, it.value().timestamp, it.value().hits,
3788 it.value().data->ref.loadRelaxed(), engineCacheCount.value(it.value().data),
3789 it.value().data->type());
3791 QFontEngine *fontEngine = it.value().data;
3793 it = engineCache.begin();
3794 while (it != engineCache.end()) {
3795 if (it.value().data == fontEngine) {
3796 fontEngine->ref.deref();
3797 it = engineCache.erase(it);
3803 Q_ASSERT(fontEngine->ref.loadRelaxed() == 0);
3804 decreaseCost(fontEngine->cache_cost);
3806 engineCacheCount.remove(fontEngine);
3808 cost_decreased =
true;
3810 }
while (cost_decreased && total_cost > max_cost);
3814#ifndef QT_NO_DEBUG_STREAM
3815QDebug operator<<(QDebug stream,
const QFont &font)
3817 QDebugStateSaver saver(stream);
3818 stream.nospace().noquote();
3821 if (stream.verbosity() == QDebug::DefaultVerbosity) {
3822 stream << font.toString() <<
")";
3826 QString fontDescription;
3827 QDebug debug(&fontDescription);
3830 const QFont defaultFont(
new QFontPrivate);
3832 for (
int property = QFont::SizeResolved; property < QFont::AllPropertiesResolved; property <<= 1) {
3833 const bool resolved = (font.resolve_mask & property) != 0;
3834 if (!resolved && stream.verbosity() == QDebug::MinimumVerbosity)
3837 #define QFONT_DEBUG_SKIP_DEFAULT(prop)
3838 if ((font.prop() == defaultFont.prop()) && stream.verbosity() == 1
)
3841 QDebugStateSaver saver(debug);
3844 case QFont::SizeResolved:
3845 if (font.pointSizeF() >= 0)
3846 debug << font.pointSizeF() <<
"pt";
3847 else if (font.pixelSize() >= 0)
3848 debug << font.pixelSize() <<
"px";
3852 case QFont::StyleHintResolved:
3854 debug.verbosity(1) << font.styleHint();
break;
3855 case QFont::StyleStrategyResolved:
3857 debug.verbosity(1) << font.styleStrategy();
break;
3858 case QFont::WeightResolved:
3859 debug.verbosity(1) << QFont::Weight(font.weight());
break;
3860 case QFont::StyleResolved:
3862 debug.verbosity(0) << font.style();
break;
3863 case QFont::UnderlineResolved:
3865 debug <<
"underline=" << font.underline();
break;
3866 case QFont::OverlineResolved:
3868 debug <<
"overline=" << font.overline();
break;
3869 case QFont::StrikeOutResolved:
3871 debug <<
"strikeOut=" << font.strikeOut();
break;
3872 case QFont::FixedPitchResolved:
3874 debug <<
"fixedPitch=" << font.fixedPitch();
break;
3875 case QFont::StretchResolved:
3877 debug.verbosity(0) << QFont::Stretch(font.stretch());
break;
3878 case QFont::KerningResolved:
3880 debug <<
"kerning=" << font.kerning();
break;
3881 case QFont::CapitalizationResolved:
3883 debug.verbosity(0) << font.capitalization();
break;
3884 case QFont::LetterSpacingResolved:
3886 debug <<
"letterSpacing=" << font.letterSpacing();
3887 debug.verbosity(0) <<
" (" << font.letterSpacingType() <<
")";
3889 case QFont::HintingPreferenceResolved:
3891 debug.verbosity(0) << font.hintingPreference();
break;
3892 case QFont::StyleNameResolved:
3894 debug <<
"styleName=" << font.styleName();
break;
3899 #undef QFONT_DEBUG_SKIP_DEFAULT
3904 if (stream.verbosity() > QDebug::MinimumVerbosity)
3905 debug.verbosity(0) <<
"resolveMask=" << QFlags<QFont::ResolveProperties>(font.resolve_mask);
3907 fontDescription.chop(2);
3909 stream << fontDescription <<
')';
3914QDebug operator<<(QDebug debug, QFont::Tag tag)
3916 QDebugStateSaver saver(debug);
3917 debug.noquote() << tag.toString();
3924#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