Support arbitrary variable axis values
This adds API to set arbitrary values for axes in variable fonts to QFont. It also implements support for the API in CoreText, FreeType (on all platforms) and DirectWrite. Note that in order for the mechanism to work with application fonts on DirectWrite, we need to keep the loader for the fonts around for the life time of the font (otherwise they cannot be reloaded with different parameters). The way this was handled before (just re-creating and throwing away the loader every time) was not ideal anyway, and this finally gives us a good reason to fix it. Fixes: QTBUG-119421 Fixes: QTBUG-117835 Fixes: QTBUG-117838 Fixes: QTBUG-117839 Change-Id: I3d1fe95fba1f9df52f34aa42e1ac5fa47dbe6d1a Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
parent
5134b3ad54
commit
7bc6f4ae8e
@ -489,6 +489,31 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>::fontEngine
|
||||
qreal scaledPointSize = fontDef.pixelSize;
|
||||
|
||||
CGAffineTransform matrix = qt_transform_from_fontdef(fontDef);
|
||||
|
||||
if (!fontDef.variableAxisValues.isEmpty()) {
|
||||
QCFType<CFMutableDictionaryRef> variations = CFDictionaryCreateMutable(nullptr,
|
||||
fontDef.variableAxisValues.size(),
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
for (auto it = fontDef.variableAxisValues.constBegin();
|
||||
it != fontDef.variableAxisValues.constEnd();
|
||||
++it) {
|
||||
const quint32 tag = it.key().value();
|
||||
const float value = it.value();
|
||||
QCFType<CFNumberRef> tagRef = CFNumberCreate(nullptr, kCFNumberIntType, &tag);
|
||||
QCFType<CFNumberRef> valueRef = CFNumberCreate(nullptr, kCFNumberFloatType, &value);
|
||||
|
||||
CFDictionarySetValue(variations, tagRef, valueRef);
|
||||
}
|
||||
QCFType<CFDictionaryRef> attributes = CFDictionaryCreate(nullptr,
|
||||
(const void **) &kCTFontVariationAttribute,
|
||||
(const void **) &variations,
|
||||
1,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, attributes);
|
||||
}
|
||||
|
||||
if (QCFType<CTFontRef> font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix))
|
||||
return new QCoreTextFontEngine(font, fontDef);
|
||||
|
||||
@ -504,7 +529,7 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const
|
||||
if (NSValue *fontDataValue = descriptorAttribute<NSValue>(descriptor, (CFStringRef)kQtFontDataAttribute)) {
|
||||
QByteArray *fontData = static_cast<QByteArray *>(fontDataValue.pointerValue);
|
||||
return QFontEngineFT::create(*fontData, fontDef.pixelSize,
|
||||
static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
|
||||
static_cast<QFont::HintingPreference>(fontDef.hintingPreference), fontDef.variableAxisValues);
|
||||
} else if (NSURL *url = descriptorAttribute<NSURL>(descriptor, kCTFontURLAttribute)) {
|
||||
QFontEngine::FaceId faceId;
|
||||
|
||||
@ -515,6 +540,8 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const
|
||||
QString styleName = QCFString(CTFontDescriptorCopyAttribute(descriptor, kCTFontStyleNameAttribute));
|
||||
faceId.index = QFreetypeFace::getFaceIndexByStyleName(faceFileName, styleName);
|
||||
|
||||
faceId.variableAxes = fontDef.variableAxisValues;
|
||||
|
||||
return QFontEngineFT::create(fontDef, faceId);
|
||||
}
|
||||
// We end up here with a descriptor does not contain Qt font data or kCTFontURLAttribute.
|
||||
@ -527,7 +554,7 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const
|
||||
template <class T>
|
||||
QFontEngine *QCoreTextFontDatabaseEngineFactory<T>::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
|
||||
{
|
||||
return T::create(fontData, pixelSize, hintingPreference);
|
||||
return T::create(fontData, pixelSize, hintingPreference, {});
|
||||
}
|
||||
|
||||
// Explicitly instantiate so that we don't need the plugin to involve FreeType
|
||||
|
@ -127,9 +127,13 @@ public:
|
||||
QByteArray m_fontData;
|
||||
};
|
||||
|
||||
QCoreTextFontEngine *QCoreTextFontEngine::create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
|
||||
QCoreTextFontEngine *QCoreTextFontEngine::create(const QByteArray &fontData,
|
||||
qreal pixelSize,
|
||||
QFont::HintingPreference hintingPreference,
|
||||
const QMap<QFont::Tag, float> &variableAxisValues)
|
||||
{
|
||||
Q_UNUSED(hintingPreference);
|
||||
Q_UNUSED(variableAxisValues);
|
||||
|
||||
QCFType<CFDataRef> fontDataReference = fontData.toRawCFData();
|
||||
QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithCFData(fontDataReference);
|
||||
@ -188,6 +192,7 @@ void QCoreTextFontEngine::init()
|
||||
face_id.index = 0;
|
||||
QCFString name = CTFontCopyName(ctfont, kCTFontUniqueNameKey);
|
||||
face_id.filename = QString::fromCFString(name).toUtf8();
|
||||
face_id.variableAxes = fontDef.variableAxisValues;
|
||||
|
||||
QCFString family = CTFontCopyFamilyName(ctfont);
|
||||
fontDef.families = QStringList(family);
|
||||
|
@ -91,7 +91,7 @@ public:
|
||||
static bool ct_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length);
|
||||
static QFont::Weight qtWeightFromCFWeight(float value);
|
||||
|
||||
static QCoreTextFontEngine *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
|
||||
static QCoreTextFontEngine *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference, const QMap<QFont::Tag, float> &variableAxisValue);
|
||||
|
||||
protected:
|
||||
QCoreTextFontEngine(const QFontDef &def);
|
||||
|
@ -295,6 +295,22 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
|
||||
FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0);
|
||||
|
||||
FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
|
||||
|
||||
if (!face_id.variableAxes.isEmpty()) {
|
||||
FT_MM_Var *var = nullptr;
|
||||
FT_Get_MM_Var(newFreetype->face, &var);
|
||||
if (var != nullptr) {
|
||||
QVarLengthArray<FT_Fixed, 16> coords(var->num_axis);
|
||||
FT_Get_Var_Design_Coordinates(face, var->num_axis, coords.data());
|
||||
for (FT_UInt i = 0; i < var->num_axis; ++i) {
|
||||
if (const auto tag = QFont::Tag::fromValue(var->axis[i].tag))
|
||||
coords[i] = FT_Fixed(face_id.variableAxes.value(*tag, coords[i]));
|
||||
}
|
||||
FT_Set_Var_Design_Coordinates(face, var->num_axis, coords.data());
|
||||
FT_Done_MM_Var(qt_getFreetype(), var);
|
||||
}
|
||||
}
|
||||
|
||||
QT_TRY {
|
||||
freetypeData->faces.insert(face_id, newFreetype.get());
|
||||
} QT_CATCH(...) {
|
||||
@ -687,27 +703,32 @@ namespace {
|
||||
fontDef.weight = QFont::Bold;
|
||||
}
|
||||
|
||||
bool initFromData(const QByteArray &fontData)
|
||||
bool initFromData(const QByteArray &fontData, const QMap<QFont::Tag, float> &variableAxisValues)
|
||||
{
|
||||
FaceId faceId;
|
||||
faceId.filename = "";
|
||||
faceId.index = 0;
|
||||
faceId.uuid = QUuid::createUuid().toByteArray();
|
||||
faceId.variableAxes = variableAxisValues;
|
||||
|
||||
return init(faceId, true, Format_None, fontData);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
|
||||
QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData,
|
||||
qreal pixelSize,
|
||||
QFont::HintingPreference hintingPreference,
|
||||
const QMap<QFont::Tag, float> &variableAxisValues)
|
||||
{
|
||||
QFontDef fontDef;
|
||||
fontDef.pixelSize = pixelSize;
|
||||
fontDef.stretch = QFont::Unstretched;
|
||||
fontDef.hintingPreference = hintingPreference;
|
||||
fontDef.variableAxisValues = variableAxisValues;
|
||||
|
||||
QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef);
|
||||
if (!fe->initFromData(fontData)) {
|
||||
if (!fe->initFromData(fontData, variableAxisValues)) {
|
||||
delete fe;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ private:
|
||||
HintStyle defaultHintStyle() const { return default_hint_style; }
|
||||
|
||||
static QFontEngineFT *create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData = QByteArray());
|
||||
static QFontEngineFT *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
|
||||
static QFontEngineFT *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference, const QMap<QFont::Tag, float> &variableAxisValue);
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -62,6 +62,7 @@ QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *us
|
||||
faceId.filename = QFile::encodeName(fontfile->fileName);
|
||||
faceId.index = fontfile->indexValue;
|
||||
faceId.instanceIndex = fontfile->instanceIndex;
|
||||
faceId.variableAxes = fontDef.variableAxisValues;
|
||||
|
||||
return QFontEngineFT::create(fontDef, faceId, fontfile->data);
|
||||
}
|
||||
@ -69,7 +70,7 @@ QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *us
|
||||
QFontEngine *QFreeTypeFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize,
|
||||
QFont::HintingPreference hintingPreference)
|
||||
{
|
||||
return QFontEngineFT::create(fontData, pixelSize, hintingPreference);
|
||||
return QFontEngineFT::create(fontData, pixelSize, hintingPreference, {});
|
||||
}
|
||||
|
||||
QStringList QFreeTypeFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont)
|
||||
|
@ -90,6 +90,9 @@ bool QFontDef::exactMatch(const QFontDef &other) const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (variableAxisValues != other.variableAxisValues)
|
||||
return false;
|
||||
|
||||
return (styleHint == other.styleHint
|
||||
&& styleStrategy == other.styleStrategy
|
||||
&& weight == other.weight
|
||||
@ -346,6 +349,24 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other)
|
||||
|
||||
if (!(mask & QFont::FeaturesResolved))
|
||||
features = other->features;
|
||||
|
||||
if (!(mask & QFont::VariableAxesResolved))
|
||||
request.variableAxisValues = other->request.variableAxisValues;
|
||||
}
|
||||
|
||||
bool QFontPrivate::hasVariableAxis(QFont::Tag tag, float value) const
|
||||
{
|
||||
return request.variableAxisValues.contains(tag) && request.variableAxisValues.value(tag) == value;
|
||||
}
|
||||
|
||||
void QFontPrivate::setVariableAxis(QFont::Tag tag, float value)
|
||||
{
|
||||
request.variableAxisValues.insert(tag, value);
|
||||
}
|
||||
|
||||
void QFontPrivate::unsetVariableAxis(QFont::Tag tag)
|
||||
{
|
||||
request.variableAxisValues.remove(tag);
|
||||
}
|
||||
|
||||
void QFontPrivate::setFeature(QFont::Tag tag, quint32 value)
|
||||
@ -1794,13 +1815,29 @@ bool QFont::operator<(const QFont &f) const
|
||||
if (d->features.size() != f.d->features.size())
|
||||
return f.d->features.size() < d->features.size();
|
||||
|
||||
auto it = d->features.constBegin();
|
||||
auto jt = f.d->features.constBegin();
|
||||
for (; it != d->features.constEnd(); ++it, ++jt) {
|
||||
if (it.key() != jt.key())
|
||||
return jt.key() < it.key();
|
||||
if (it.value() != jt.value())
|
||||
return jt.value() < it.value();
|
||||
{
|
||||
auto it = d->features.constBegin();
|
||||
auto jt = f.d->features.constBegin();
|
||||
for (; it != d->features.constEnd(); ++it, ++jt) {
|
||||
if (it.key() != jt.key())
|
||||
return jt.key() < it.key();
|
||||
if (it.value() != jt.value())
|
||||
return jt.value() < it.value();
|
||||
}
|
||||
}
|
||||
|
||||
if (r1.variableAxisValues.size() != r2.variableAxisValues.size())
|
||||
return r1.variableAxisValues.size() < r2.variableAxisValues.size();
|
||||
|
||||
{
|
||||
auto it = r1.variableAxisValues.constBegin();
|
||||
auto jt = r2.variableAxisValues.constBegin();
|
||||
for (; it != r1.variableAxisValues.constEnd(); ++it, ++jt) {
|
||||
if (it.key() != jt.key())
|
||||
return jt.key() < it.key();
|
||||
if (it.value() != jt.value())
|
||||
return jt.value() < it.value();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -2357,6 +2394,142 @@ std::optional<QFont::Tag> QFont::Tag::fromString(QAnyStringView view) noexcept
|
||||
Data stream operators for QFont::Tag.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\since 6.7
|
||||
|
||||
Applies a \a value to the variable axis corresponding to \a tag.
|
||||
|
||||
Variable fonts provide a way to store multiple variations (with different weights, widths
|
||||
or styles) in the same font file. The variations are given as floating point values for
|
||||
a pre-defined set of parameters, called "variable axes". Specific instances are typically
|
||||
given names by the font designer, and, in Qt, these can be selected using setStyleName()
|
||||
just like traditional sub-families.
|
||||
|
||||
In some cases, it is also useful to provide arbitrary values for the different axes. For
|
||||
instance, if a font has a Regular and Bold sub-family, you may want a weight in-between these.
|
||||
You could then manually request this by supplying a custom value for the "wght" axis in the
|
||||
font.
|
||||
|
||||
\code
|
||||
QFont font;
|
||||
font.setVariableAxis("wght", (QFont::Normal + QFont::Bold) / 2.0f);
|
||||
\endcode
|
||||
|
||||
If the "wght" axis is supported by the font and the given value is within its defined range,
|
||||
a font corresponding to the weight 550.0 will be provided.
|
||||
|
||||
There are a few standard axes than many fonts provide, such as "wght" (weight), "wdth" (width),
|
||||
"ital" (italic) and "opsz" (optical size). They each have indivdual ranges defined in the font
|
||||
itself. For instance, "wght" may span from 100 to 900 (QFont::Thin to QFont::Black) whereas
|
||||
"ital" can span from 0 to 1 (from not italic to fully italic).
|
||||
|
||||
A font may also choose to define custom axes; the only limitation is that the name has to
|
||||
meet the requirements for a QFont::Tag (sequence of four latin-1 characters.)
|
||||
|
||||
By default, no variable axes are set.
|
||||
|
||||
\note In order to use variable axes on Windows, the application has to run with either the
|
||||
FreeType or DirectWrite font databases. See the documentation for
|
||||
QGuiApplication::QGuiApplication() for more information on how to select these technologies.
|
||||
|
||||
\sa unsetVariableAxis
|
||||
*/
|
||||
void QFont::setVariableAxis(Tag tag, float value)
|
||||
{
|
||||
if (tag.isValid()) {
|
||||
if (resolve_mask & QFont::VariableAxesResolved && d->hasVariableAxis(tag, value))
|
||||
return;
|
||||
|
||||
detach();
|
||||
|
||||
d->setVariableAxis(tag, value);
|
||||
resolve_mask |= QFont::VariableAxesResolved;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.7
|
||||
|
||||
Unsets a previously set variable axis value given by \a tag.
|
||||
|
||||
\note If no value has previously been given for this tag, the QFont will still consider its
|
||||
variable axes as set when resolving against other QFont values.
|
||||
|
||||
\sa setVariableAxis
|
||||
*/
|
||||
void QFont::unsetVariableAxis(Tag tag)
|
||||
{
|
||||
if (tag.isValid()) {
|
||||
detach();
|
||||
|
||||
d->unsetVariableAxis(tag);
|
||||
resolve_mask |= QFont::VariableAxesResolved;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.7
|
||||
|
||||
Returns a list of tags for all variable axes currently set on this QFont.
|
||||
|
||||
See \l{QFont::}{setVariableAxis()} for more details on variable axes.
|
||||
|
||||
\sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), isVariableAxisSet(), clearVariableAxes()
|
||||
*/
|
||||
QList<QFont::Tag> QFont::variableAxisTags() const
|
||||
{
|
||||
return d->request.variableAxisValues.keys();
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.7
|
||||
|
||||
Returns the value set for a specific variable axis \a tag. If the tag has not been set, 0.0 will
|
||||
be returned instead.
|
||||
|
||||
See \l{QFont::}{setVariableAxis()} for more details on variable axes.
|
||||
|
||||
\sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), isVariableAxisSet(), clearVariableAxes()
|
||||
*/
|
||||
float QFont::variableAxisValue(Tag tag) const
|
||||
{
|
||||
return d->request.variableAxisValues.value(tag);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.7
|
||||
|
||||
Returns true if a value for the variable axis given by \a tag has been set on the QFont,
|
||||
otherwise returns false.
|
||||
|
||||
See \l{QFont::}{setVariableAxis()} for more details on font variable axes.
|
||||
|
||||
\sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), variableAxisValue(), clearVariableAxes()
|
||||
*/
|
||||
bool QFont::isVariableAxisSet(Tag tag) const
|
||||
{
|
||||
return d->request.variableAxisValues.contains(tag);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.7
|
||||
|
||||
Clears any previously set variable axis values on the QFont.
|
||||
|
||||
See \l{QFont::}{setVariableAxis()} for more details on variable axes.
|
||||
|
||||
\sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), isVariableAxisSet(), variableAxisValue()
|
||||
*/
|
||||
void QFont::clearVariableAxes()
|
||||
{
|
||||
if (d->request.variableAxisValues.isEmpty())
|
||||
return;
|
||||
|
||||
detach();
|
||||
d->request.variableAxisValues.clear();
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\since 6.7
|
||||
\overload
|
||||
@ -2626,6 +2799,8 @@ QDataStream &operator<<(QDataStream &s, const QFont &font)
|
||||
}
|
||||
if (s.version() >= QDataStream::Qt_6_6)
|
||||
s << font.d->features;
|
||||
if (s.version() >= QDataStream::Qt_6_7)
|
||||
s << font.d->request.variableAxisValues;
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -2744,6 +2919,10 @@ QDataStream &operator>>(QDataStream &s, QFont &font)
|
||||
font.d->features.clear();
|
||||
s >> font.d->features;
|
||||
}
|
||||
if (s.version() >= QDataStream::Qt_6_7) {
|
||||
font.d->request.variableAxisValues.clear();
|
||||
s >> font.d->request.variableAxisValues;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -128,7 +128,8 @@ public:
|
||||
StyleNameResolved = 0x10000,
|
||||
FamiliesResolved = 0x20000,
|
||||
FeaturesResolved = 0x40000,
|
||||
AllPropertiesResolved = 0x7ffff
|
||||
VariableAxesResolved = 0x80000,
|
||||
AllPropertiesResolved = 0xfffff
|
||||
};
|
||||
Q_ENUM(ResolveProperties)
|
||||
|
||||
@ -271,6 +272,13 @@ public:
|
||||
QList<Tag> featureTags() const;
|
||||
void clearFeatures();
|
||||
|
||||
void setVariableAxis(Tag tag, float value);
|
||||
void unsetVariableAxis(Tag tag);
|
||||
bool isVariableAxisSet(Tag tag) const;
|
||||
float variableAxisValue(Tag tag) const;
|
||||
void clearVariableAxes();
|
||||
QList<Tag> variableAxisTags() const;
|
||||
|
||||
// dupicated from QFontInfo
|
||||
bool exactMatch() const;
|
||||
|
||||
|
@ -55,6 +55,7 @@ struct QFontDef
|
||||
QString styleName;
|
||||
|
||||
QStringList fallBackFamilies;
|
||||
QMap<QFont::Tag, float> variableAxisValues;
|
||||
|
||||
qreal pointSize;
|
||||
qreal pixelSize;
|
||||
@ -85,6 +86,7 @@ struct QFontDef
|
||||
&& families == other.families
|
||||
&& styleName == other.styleName
|
||||
&& hintingPreference == other.hintingPreference
|
||||
&& variableAxisValues == other.variableAxisValues
|
||||
;
|
||||
}
|
||||
inline bool operator<(const QFontDef &other) const
|
||||
@ -103,6 +105,22 @@ struct QFontDef
|
||||
|
||||
if (ignorePitch != other.ignorePitch) return ignorePitch < other.ignorePitch;
|
||||
if (fixedPitch != other.fixedPitch) return fixedPitch < other.fixedPitch;
|
||||
if (variableAxisValues != other.variableAxisValues) {
|
||||
if (variableAxisValues.size() != other.variableAxisValues.size())
|
||||
return variableAxisValues.size() < other.variableAxisValues.size();
|
||||
|
||||
{
|
||||
auto it = variableAxisValues.constBegin();
|
||||
auto jt = other.variableAxisValues.constBegin();
|
||||
for (; it != variableAxisValues.constEnd(); ++it, ++jt) {
|
||||
if (it.key() != jt.key())
|
||||
return jt.key() < it.key();
|
||||
if (it.value() != jt.value())
|
||||
return jt.value() < it.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@ -120,7 +138,9 @@ inline size_t qHash(const QFontDef &fd, size_t seed = 0) noexcept
|
||||
fd.fixedPitch,
|
||||
fd.families,
|
||||
fd.styleName,
|
||||
fd.hintingPreference);
|
||||
fd.hintingPreference,
|
||||
fd.variableAxisValues.keys(),
|
||||
fd.variableAxisValues.values());
|
||||
}
|
||||
|
||||
class QFontEngineData
|
||||
@ -182,6 +202,10 @@ public:
|
||||
void setFeature(QFont::Tag tag, quint32 value);
|
||||
void unsetFeature(QFont::Tag tag);
|
||||
|
||||
void setVariableAxis(QFont::Tag tag, float value);
|
||||
void unsetVariableAxis(QFont::Tag tag);
|
||||
bool hasVariableAxis(QFont::Tag tag, float value) const;
|
||||
|
||||
private:
|
||||
QFontPrivate &operator=(const QFontPrivate &) { return *this; }
|
||||
};
|
||||
|
@ -126,6 +126,7 @@ public:
|
||||
int index;
|
||||
int instanceIndex;
|
||||
int encoding;
|
||||
QMap<QFont::Tag, float> variableAxes;
|
||||
};
|
||||
virtual FaceId faceId() const { return FaceId(); }
|
||||
enum SynthesizedFlags {
|
||||
@ -368,13 +369,14 @@ inline bool operator ==(const QFontEngine::FaceId &f1, const QFontEngine::FaceId
|
||||
&& f1.encoding == f2.encoding
|
||||
&& f1.filename == f2.filename
|
||||
&& f1.uuid == f2.uuid
|
||||
&& f1.instanceIndex == f2.instanceIndex;
|
||||
&& f1.instanceIndex == f2.instanceIndex
|
||||
&& f1.variableAxes == f2.variableAxes;
|
||||
}
|
||||
|
||||
inline size_t qHash(const QFontEngine::FaceId &f, size_t seed = 0)
|
||||
noexcept(noexcept(qHash(f.filename)))
|
||||
{
|
||||
return qHashMulti(seed, f.filename, f.uuid, f.index, f.instanceIndex, f.encoding);
|
||||
return qHashMulti(seed, f.filename, f.uuid, f.index, f.instanceIndex, f.encoding, f.variableAxes.keys(), f.variableAxes.values());
|
||||
}
|
||||
|
||||
|
||||
|
@ -722,6 +722,7 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr)
|
||||
fid.filename = QFile::encodeName(fontfile->fileName);
|
||||
fid.index = fontfile->indexValue;
|
||||
fid.instanceIndex = fontfile->instanceIndex;
|
||||
fid.variableAxes = f.variableAxisValues;
|
||||
|
||||
// FIXME: Unify with logic in QFontEngineFT::create()
|
||||
QFontEngineFT *engine = new QFontEngineFT(f);
|
||||
|
@ -18,6 +18,32 @@ QT_BEGIN_NAMESPACE
|
||||
// Defined in gui/text/qfontdatabase.cpp
|
||||
Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script);
|
||||
|
||||
template<typename T>
|
||||
struct DirectWriteScope {
|
||||
DirectWriteScope(T *res = nullptr) : m_res(res) {}
|
||||
~DirectWriteScope() {
|
||||
if (m_res != nullptr)
|
||||
m_res->Release();
|
||||
}
|
||||
|
||||
T **operator&()
|
||||
{
|
||||
return &m_res;
|
||||
}
|
||||
|
||||
T *operator->()
|
||||
{
|
||||
return m_res;
|
||||
}
|
||||
|
||||
T *operator*() {
|
||||
return m_res;
|
||||
}
|
||||
|
||||
private:
|
||||
T *m_res;
|
||||
};
|
||||
|
||||
QWindowsDirectWriteFontDatabase::QWindowsDirectWriteFontDatabase()
|
||||
{
|
||||
qCDebug(lcQpaFonts) << "Creating DirectWrite database";
|
||||
@ -217,6 +243,37 @@ QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QFontDef &fontDef
|
||||
IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
|
||||
Q_ASSERT(face != nullptr);
|
||||
|
||||
DirectWriteScope<IDWriteFontFace5> newFace;
|
||||
if (!fontDef.variableAxisValues.isEmpty()) {
|
||||
DirectWriteScope<IDWriteFontFace5> face5;
|
||||
if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace5),
|
||||
reinterpret_cast<void **>(&face5)))) {
|
||||
DirectWriteScope<IDWriteFontResource> font;
|
||||
if (SUCCEEDED(face5->GetFontResource(&font))) {
|
||||
UINT32 fontAxisCount = font->GetFontAxisCount();
|
||||
|
||||
QVarLengthArray<DWRITE_FONT_AXIS_VALUE, 8> fontAxisValues(fontAxisCount);
|
||||
if (SUCCEEDED(face5->GetFontAxisValues(fontAxisValues.data(), fontAxisCount))) {
|
||||
for (UINT32 i = 0; i < fontAxisCount; ++i) {
|
||||
if (auto maybeTag = QFont::Tag::fromValue(qToBigEndian<UINT32>(fontAxisValues[i].axisTag))) {
|
||||
if (fontDef.variableAxisValues.contains(*maybeTag))
|
||||
fontAxisValues[i].value = fontDef.variableAxisValues.value(*maybeTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(font->CreateFontFace(DWRITE_FONT_SIMULATIONS_NONE,
|
||||
fontAxisValues.data(),
|
||||
fontAxisCount,
|
||||
&newFace))) {
|
||||
face = *newFace;
|
||||
} else {
|
||||
qCWarning(lcQpaFonts) << "DirectWrite: Can't create font face for variable axis values";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(face, fontDef.pixelSize, data());
|
||||
fontEngine->initFontInfo(fontDef, defaultVerticalDPI());
|
||||
|
||||
|
@ -724,6 +724,7 @@ void QWindowsFontDatabase::populateFontDatabase()
|
||||
|
||||
void QWindowsFontDatabase::invalidate()
|
||||
{
|
||||
QWindowsFontDatabaseBase::invalidate();
|
||||
removeApplicationFonts();
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ public:
|
||||
|
||||
void populateFontDatabase() override;
|
||||
void invalidate() override;
|
||||
void removeApplicationFonts();
|
||||
|
||||
void populateFamily(const QString &familyName) override;
|
||||
bool populateFamilyAliases(const QString &missingFamily) override;
|
||||
@ -74,8 +75,9 @@ public:
|
||||
static void debugFormat(QDebug &d, const LOGFONT &lf);
|
||||
#endif // !QT_NO_DEBUG_STREAM
|
||||
|
||||
|
||||
private:
|
||||
void removeApplicationFonts();
|
||||
|
||||
void addDefaultEUDCFont();
|
||||
|
||||
struct WinApplicationFont {
|
||||
|
@ -359,10 +359,10 @@ namespace {
|
||||
{
|
||||
}
|
||||
|
||||
inline void addKey(const void *key, const QByteArray &fontData)
|
||||
inline void addKey(const QByteArray &fontData)
|
||||
{
|
||||
Q_ASSERT(!m_fontDatas.contains(key));
|
||||
m_fontDatas.insert(key, fontData);
|
||||
if (!m_fontDatas.contains(fontData.data()))
|
||||
m_fontDatas.insert(fontData.data(), fontData);
|
||||
}
|
||||
|
||||
inline void removeKey(const void *key)
|
||||
@ -378,6 +378,11 @@ namespace {
|
||||
UINT32 fontFileReferenceKeySize,
|
||||
OUT IDWriteFontFileStream **fontFileStream) override;
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_fontDatas.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
ULONG m_referenceCount;
|
||||
QHash<const void *, QByteArray> m_fontDatas;
|
||||
@ -435,53 +440,63 @@ namespace {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
class CustomFontFileLoader
|
||||
{
|
||||
public:
|
||||
CustomFontFileLoader(IDWriteFactory *factory)
|
||||
{
|
||||
m_directWriteFactory = factory;
|
||||
|
||||
if (m_directWriteFactory) {
|
||||
m_directWriteFactory->AddRef();
|
||||
|
||||
m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
|
||||
m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
|
||||
}
|
||||
}
|
||||
|
||||
~CustomFontFileLoader()
|
||||
{
|
||||
if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr)
|
||||
m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
|
||||
|
||||
if (m_directWriteFactory != nullptr)
|
||||
m_directWriteFactory->Release();
|
||||
}
|
||||
|
||||
void addKey(const void *key, const QByteArray &fontData)
|
||||
{
|
||||
if (m_directWriteFontFileLoader != nullptr)
|
||||
m_directWriteFontFileLoader->addKey(key, fontData);
|
||||
}
|
||||
|
||||
void removeKey(const void *key)
|
||||
{
|
||||
if (m_directWriteFontFileLoader != nullptr)
|
||||
m_directWriteFontFileLoader->removeKey(key);
|
||||
}
|
||||
|
||||
IDWriteFontFileLoader *loader() const
|
||||
{
|
||||
return m_directWriteFontFileLoader;
|
||||
}
|
||||
|
||||
private:
|
||||
IDWriteFactory *m_directWriteFactory = nullptr;
|
||||
DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr;
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
class QCustomFontFileLoader
|
||||
{
|
||||
public:
|
||||
QCustomFontFileLoader(IDWriteFactory *factory)
|
||||
{
|
||||
m_directWriteFactory = factory;
|
||||
|
||||
if (m_directWriteFactory) {
|
||||
m_directWriteFactory->AddRef();
|
||||
|
||||
m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
|
||||
m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
|
||||
}
|
||||
}
|
||||
|
||||
~QCustomFontFileLoader()
|
||||
{
|
||||
clear();
|
||||
|
||||
if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr)
|
||||
m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
|
||||
|
||||
if (m_directWriteFactory != nullptr)
|
||||
m_directWriteFactory->Release();
|
||||
}
|
||||
|
||||
void addKey(const QByteArray &fontData)
|
||||
{
|
||||
if (m_directWriteFontFileLoader != nullptr)
|
||||
m_directWriteFontFileLoader->addKey(fontData);
|
||||
}
|
||||
|
||||
void removeKey(const void *key)
|
||||
{
|
||||
if (m_directWriteFontFileLoader != nullptr)
|
||||
m_directWriteFontFileLoader->removeKey(key);
|
||||
}
|
||||
|
||||
IDWriteFontFileLoader *loader() const
|
||||
{
|
||||
return m_directWriteFontFileLoader;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_directWriteFontFileLoader != nullptr)
|
||||
m_directWriteFontFileLoader->clear();
|
||||
}
|
||||
|
||||
private:
|
||||
IDWriteFactory *m_directWriteFactory = nullptr;
|
||||
DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr;
|
||||
};
|
||||
|
||||
|
||||
#endif // directwrite && direct2d
|
||||
|
||||
|
||||
@ -704,6 +719,11 @@ IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArra
|
||||
return faces.isEmpty() ? nullptr : faces.first();
|
||||
}
|
||||
|
||||
void QWindowsFontDatabaseBase::invalidate()
|
||||
{
|
||||
m_fontFileLoader.reset(nullptr);
|
||||
}
|
||||
|
||||
QList<IDWriteFontFace *> QWindowsFontDatabaseBase::createDirectWriteFaces(const QByteArray &fontData,
|
||||
bool queryVariations) const
|
||||
{
|
||||
@ -714,15 +734,17 @@ QList<IDWriteFontFace *> QWindowsFontDatabaseBase::createDirectWriteFaces(const
|
||||
return ret;
|
||||
}
|
||||
|
||||
CustomFontFileLoader fontFileLoader(fontEngineData->directWriteFactory);
|
||||
fontFileLoader.addKey(this, fontData);
|
||||
if (m_fontFileLoader == nullptr)
|
||||
m_fontFileLoader.reset(new QCustomFontFileLoader(fontEngineData->directWriteFactory));
|
||||
|
||||
m_fontFileLoader->addKey(fontData);
|
||||
|
||||
IDWriteFontFile *fontFile = nullptr;
|
||||
const void *key = this;
|
||||
const void *key = fontData.data();
|
||||
|
||||
HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key,
|
||||
sizeof(void *),
|
||||
fontFileLoader.loader(),
|
||||
m_fontFileLoader->loader(),
|
||||
&fontFile);
|
||||
if (FAILED(hres)) {
|
||||
qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__);
|
||||
@ -793,6 +815,7 @@ QList<IDWriteFontFace *> QWindowsFontDatabaseBase::createDirectWriteFaces(const
|
||||
}
|
||||
|
||||
fontFile->Release();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif // directwrite && direct2d
|
||||
|
@ -29,6 +29,10 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if QT_CONFIG(directwrite)
|
||||
class QCustomFontFileLoader;
|
||||
#endif
|
||||
|
||||
class QWindowsFontEngineData
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(QWindowsFontEngineData)
|
||||
@ -56,6 +60,8 @@ public:
|
||||
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
|
||||
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
|
||||
|
||||
void invalidate() override;
|
||||
|
||||
static int defaultVerticalDPI();
|
||||
|
||||
static QSharedPointer<QWindowsFontEngineData> data();
|
||||
@ -98,6 +104,10 @@ protected:
|
||||
|
||||
private:
|
||||
static bool init(QSharedPointer<QWindowsFontEngineData> data);
|
||||
|
||||
#if QT_CONFIG(directwrite)
|
||||
mutable std::unique_ptr<QCustomFontFileLoader> m_fontFileLoader;
|
||||
#endif
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -1015,6 +1015,8 @@ void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request,
|
||||
else if (fontDef.pixelSize == -1)
|
||||
fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
|
||||
|
||||
m_faceId.variableAxes = request.variableAxisValues;
|
||||
|
||||
#if QT_CONFIG(directwrite3)
|
||||
IDWriteFontFace3 *face3 = nullptr;
|
||||
if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
|
||||
|
Loading…
x
Reference in New Issue
Block a user