Add support for font features and variable axes to QTextCharFormat

These can be set on the font directly, but had not been added to
QTextCharFormat, so there would be no way to override them by
formatting in a rich text document.

Fixes: QTBUG-134060
Change-Id: I4494e24cb9b99d84fb376ba895e2461fc3cd054b
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2025-06-02 12:38:55 +02:00
parent 9b9a2398f3
commit fb89d498a9
5 changed files with 267 additions and 10 deletions

View File

@ -91,7 +91,7 @@ public:
Qt_6_8 = Qt_6_7,
Qt_6_9 = Qt_6_7,
Qt_6_10 = 23,
Qt_6_11 = Qt_6_10,
Qt_6_11 = 24,
Qt_DefaultCompiledVersion = Qt_6_11
#if QT_VERSION >= QT_VERSION_CHECK(6, 12, 0)
#error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion

View File

@ -388,6 +388,20 @@ void QTextFormatPrivate::recalcFont() const
case QTextFormat::FontKerning:
f.setKerning(props.at(i).value.toBool());
break;
case QTextFormat::FontFeatures:
{
const auto fontFeatures = props.at(i).value.value<QHash<QFont::Tag, quint32>>();
for (auto it = fontFeatures.constBegin(); it != fontFeatures.constEnd(); ++it)
f.setFeature(it.key(), it.value());
break;
}
case QTextFormat::FontVariableAxes:
{
const auto fontVariableAxes = props.at(i).value.value<QHash<QFont::Tag, float>>();
for (auto it = fontVariableAxes.constBegin(); it != fontVariableAxes.constEnd(); ++it)
f.setVariableAxis(it.key(), it.value());
break;
}
default:
break;
}
@ -404,6 +418,16 @@ void QTextFormatPrivate::recalcFont() const
Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QTextFormat &fmt)
{
QMap<int, QVariant> properties = fmt.properties();
if (stream.version() < QDataStream::Qt_6_11) {
auto it = properties.constFind(QTextFormat::FontFeatures);
if (it != properties.cend())
properties.erase(it);
it = properties.constFind(QTextFormat::FontVariableAxes);
if (it != properties.cend())
properties.erase(it);
}
if (stream.version() < QDataStream::Qt_6_0) {
auto it = properties.constFind(QTextFormat::FontLetterSpacingType);
if (it != properties.cend()) {
@ -447,14 +471,17 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
for (QMap<qint32, QVariant>::ConstIterator it = properties.constBegin();
it != properties.constEnd(); ++it) {
qint32 key = it.key();
if (key == QTextFormat::OldFontLetterSpacingType)
key = QTextFormat::FontLetterSpacingType;
else if (key == QTextFormat::OldFontStretch)
key = QTextFormat::FontStretch;
else if (key == QTextFormat::OldTextUnderlineColor)
key = QTextFormat::TextUnderlineColor;
else if (key == QTextFormat::OldFontFamily)
key = QTextFormat::FontFamilies;
if (stream.version() < QDataStream::Qt_6_0) {
if (key == QTextFormat::OldFontLetterSpacingType)
key = QTextFormat::FontLetterSpacingType;
else if (key == QTextFormat::OldFontStretch)
key = QTextFormat::FontStretch;
else if (key == QTextFormat::OldTextUnderlineColor)
key = QTextFormat::TextUnderlineColor;
else if (key == QTextFormat::OldFontFamily)
key = QTextFormat::FontFamilies;
}
fmt.d->insertProperty(key, it.value());
}
@ -653,6 +680,10 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextTableCellFormat &
\value FontKerning Specifies whether the font has kerning turned on.
\value FontHintingPreference Controls the use of hinting according to values
of the QFont::HintingPreference enum.
\value FontFeatures [since 6.11] Assigns integer numbers to typographical features. See
\l{QFont::setFeature()} for additional information.
\value FontVariableAxes [since 6.11] Assigns floating point numbers to variable axes in variable
fonts. See \l{QFont::setVariableAxis()} for additional information.
\omitvalue FirstFontProperty
\omitvalue LastFontProperty
@ -1809,6 +1840,55 @@ void QTextCharFormat::setUnderlineStyle(UnderlineStyle style)
\sa font(), QFont::hintingPreference()
*/
/*!
\since 6.11
Sets the typographical features of the text format's font to be \a fontFeatures.
\sa QFont::setFeature()
*/
void QTextCharFormat::setFontFeatures(const QHash<QFont::Tag, quint32> &fontFeatures)
{
setProperty(FontFeatures, QVariant::fromValue(fontFeatures));
}
/*!
\since 6.11
Gets the typographical features of the text format's font.
\sa setFontFeatures()
*/
QHash<QFont::Tag, quint32> QTextCharFormat::fontFeatures() const
{
return property(FontFeatures).value<QHash<QFont::Tag, quint32>>();
}
/*!
\since 6.11
Sets the variable axes of the text format's font to be \a fontVariableAxes.
\sa QFont::setVariableAxis()
*/
void QTextCharFormat::setFontVariableAxes(const QHash<QFont::Tag, float> &fontVariableAxes)
{
setProperty(FontVariableAxes, QVariant::fromValue(fontVariableAxes));
}
/*!
\since 6.11
Gets the variable axes of the text format's font.
\sa setFontVariableAxes()
*/
QHash<QFont::Tag, float> QTextCharFormat::fontVariableAxes() const
{
return property(FontVariableAxes).value<QHash<QFont::Tag, float>>();
}
/*!
\fn QPen QTextCharFormat::textOutline() const
@ -2145,6 +2225,22 @@ void QTextCharFormat::setFont(const QFont &font, FontPropertiesInheritanceBehavi
setFontHintingPreference(font.hintingPreference());
if (mask & QFont::KerningResolved)
setFontKerning(font.kerning());
if (mask & QFont::FeaturesResolved) {
const auto tags = font.featureTags();
QHash<QFont::Tag, quint32> fontFeatures;
for (QFont::Tag tag : tags)
fontFeatures.insert(tag, font.featureValue(tag));
setFontFeatures(fontFeatures);
}
if (mask & QFont::VariableAxesResolved) {
const auto tags = font.variableAxisTags();
QHash<QFont::Tag, float> fontVariableAxes;
for (QFont::Tag tag : tags)
fontVariableAxes.insert(tag, font.variableAxisValue(tag));
setFontVariableAxes(fontVariableAxes);
}
}
/*!

View File

@ -14,6 +14,7 @@
#include <QtCore/qlist.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qvariant.h>
#include <QtCore/qhash.h>
QT_BEGIN_NAMESPACE
@ -159,7 +160,9 @@ public:
FontStrikeOut = 0x2007,
FontFixedPitch = 0x2008,
FontPixelSize = 0x2009,
LastFontProperty = FontPixelSize,
FontFeatures = 0x2010, // Note: Same as OldTextUnderlineColor
FontVariableAxes = 0x2011,
LastFontProperty = FontVariableAxes,
TextUnderlineColor = 0x2020,
TextVerticalAlignment = 0x2021,
@ -518,6 +521,11 @@ public:
return static_cast<QFont::HintingPreference>(intProperty(FontHintingPreference));
}
void setFontFeatures(const QHash<QFont::Tag, quint32> &fontFeatures);
QHash<QFont::Tag, quint32> fontFeatures() const;
void setFontVariableAxes(const QHash<QFont::Tag, float> &fontVariableAxes);
QHash<QFont::Tag, float> fontVariableAxes() const;
inline void setFontKerning(bool enable)
{ setProperty(FontKerning, enable); }
inline bool fontKerning() const

View File

@ -301,6 +301,7 @@ static constexpr int NColorRoles[] = {
QPalette::Accent + 1, // Qt_6_6
QPalette::Accent + 1, // Qt_6_7
QPalette::Accent + 1, // Qt_6_10
QPalette::Accent + 1, // Qt_6_11
};
// +1, because we start from "No Version"

View File

@ -45,6 +45,8 @@ private slots:
void setFont_collection_data();
void setFont_collection();
void clearCollection();
void setFontFeatures();
void setFontVariableAxes();
#ifndef QT_NO_DATASTREAM
void dataStreamCompatibility();
@ -667,6 +669,89 @@ void tst_QTextFormat::clearCollection()
QCOMPARE(collection.defaultFont(), f); // kept, QTextDocument::clear or setPlainText should not reset the font set by setDefaultFont
}
void tst_QTextFormat::setFontFeatures()
{
{
QFont font;
font.setFeature("abcd", 1234);
font.setFeature("efgh", 5678);
QTextCharFormat format;
format.setFont(font);
QFont resolvedFont = format.font();
QCOMPARE(resolvedFont.featureTags().size(), 2);
QCOMPARE(resolvedFont.featureValue("abcd"), 1234);
QCOMPARE(resolvedFont.featureValue("efgh"), 5678);
QHash<QFont::Tag, quint32> features = format.fontFeatures();
QCOMPARE(features.size(), 2);
QCOMPARE(features.value("abcd"), 1234);
QCOMPARE(features.value("efgh"), 5678);
}
{
QTextCharFormat format;
QHash<QFont::Tag, quint32> features;
features.insert("abcd", 4321);
features.insert("efgh", 8765);
format.setFontFeatures(features);
QFont resolvedFont = format.font();
QCOMPARE(resolvedFont.featureTags().size(), 2);
QCOMPARE(resolvedFont.featureValue("abcd"), 4321);
QCOMPARE(resolvedFont.featureValue("efgh"), 8765);
features = format.fontFeatures();
QCOMPARE(features.size(), 2);
QCOMPARE(features.value("abcd"), 4321);
QCOMPARE(features.value("efgh"), 8765);
}
}
void tst_QTextFormat::setFontVariableAxes()
{
{
QFont font;
font.setVariableAxis("abcd", 12.25);
font.setVariableAxis("efgh", 13.25);
QTextCharFormat format;
format.setFont(font);
QFont resolvedFont = format.font();
QCOMPARE(resolvedFont.variableAxisTags().size(), 2);
QCOMPARE(resolvedFont.variableAxisValue("abcd"), 12.25);
QCOMPARE(resolvedFont.variableAxisValue("efgh"), 13.25);
QHash<QFont::Tag, float> axes = format.fontVariableAxes();
QCOMPARE(axes.size(), 2);
QCOMPARE(axes.value("abcd"), 12.25);
QCOMPARE(axes.value("efgh"), 13.25);
}
{
QTextCharFormat format;
QHash<QFont::Tag, float> axes;
axes.insert("abcd", 12.25);
axes.insert("efgh", 13.25);
format.setFontVariableAxes(axes);
QFont resolvedFont = format.font();
QCOMPARE(resolvedFont.variableAxisTags().size(), 2);
QCOMPARE(resolvedFont.variableAxisValue("abcd"), 12.25);
QCOMPARE(resolvedFont.variableAxisValue("efgh"), 13.25);
axes = format.fontVariableAxes();
QCOMPARE(axes.size(), 2);
QCOMPARE(axes.value("abcd"), 12.25);
QCOMPARE(axes.value("efgh"), 13.25);
}
}
#ifndef QT_NO_DATASTREAM
void tst_QTextFormat::dataStreamCompatibility()
{
@ -795,6 +880,73 @@ void tst_QTextFormat::dataStreamCompatibility()
}
}
// Don't mix up FontFeatures and OldTextUnderlineColor
memory.clear();
{
{
QBuffer buffer(&memory);
buffer.open(QIODevice::WriteOnly);
QFont font;
font.setFeature("abcd", 1234);
QTextCharFormat format;
format.setFont(font);
QDataStream stream(&buffer);
stream << format;
}
{
QBuffer buffer(&memory);
buffer.open(QIODevice::ReadOnly);
QDataStream stream(&buffer);
QTextFormat other;
stream >> other;
QMap<int, QVariant> properties = other.properties();
QVERIFY(properties.contains(QTextFormat::FontFeatures));
auto features = other.property(QTextFormat::FontFeatures).value<QHash<QFont::Tag, quint32>>();
QCOMPARE(features.value("abcd"), 1234);
}
}
memory.clear();
{
{
QBuffer buffer(&memory);
buffer.open(QIODevice::WriteOnly);
QFont font;
font.setFeature("abcd", 1234);
QTextCharFormat format;
format.setFont(font);
QDataStream stream(&buffer);
stream.setVersion(QDataStream::Qt_5_15);
stream << format;
}
{
QBuffer buffer(&memory);
buffer.open(QIODevice::ReadOnly);
QDataStream stream(&buffer);
stream.setVersion(QDataStream::Qt_5_15);
QTextFormat other;
stream >> other;
QMap<int, QVariant> properties = other.properties();
QVERIFY(!properties.contains(QTextFormat::FontFeatures));
QVERIFY(!properties.contains(QTextFormat::OldTextUnderlineColor));
}
}
}
#endif // QT_NO_DATASTREAM