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:
Eskil Abrahamsen Blomfeldt 2023-11-01 15:07:23 +01:00
parent 5134b3ad54
commit 7bc6f4ae8e
17 changed files with 436 additions and 73 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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:

View File

@ -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)

View File

@ -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;
}

View File

@ -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;

View File

@ -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; }
};

View File

@ -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());
}

View File

@ -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);

View File

@ -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());

View File

@ -724,6 +724,7 @@ void QWindowsFontDatabase::populateFontDatabase()
void QWindowsFontDatabase::invalidate()
{
QWindowsFontDatabaseBase::invalidate();
removeApplicationFonts();
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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),