diff options
Diffstat (limited to 'src/gui/text/freetype')
-rw-r--r-- | src/gui/text/freetype/qfontengine_ft.cpp | 372 | ||||
-rw-r--r-- | src/gui/text/freetype/qfontengine_ft_p.h | 52 | ||||
-rw-r--r-- | src/gui/text/freetype/qfreetypefontdatabase.cpp | 239 | ||||
-rw-r--r-- | src/gui/text/freetype/qfreetypefontdatabase_p.h | 55 |
4 files changed, 486 insertions, 232 deletions
diff --git a/src/gui/text/freetype/qfontengine_ft.cpp b/src/gui/text/freetype/qfontengine_ft.cpp index 12ba46b7ed..d3791f1f6e 100644 --- a/src/gui/text/freetype/qfontengine_ft.cpp +++ b/src/gui/text/freetype/qfontengine_ft.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qdir.h" #include "qmetatype.h" @@ -48,6 +12,7 @@ #include <qscreen.h> #include <qpa/qplatformscreen.h> #include <QtCore/QUuid> +#include <QtCore/QLoggingCategory> #include <QtGui/QPainterPath> #ifndef QT_NO_FREETYPE @@ -59,6 +24,8 @@ #include <qmath.h> #include <qendian.h> +#include <memory> + #include <ft2build.h> #include FT_FREETYPE_H #include FT_OUTLINE_H @@ -68,6 +35,7 @@ #include FT_GLYPH_H #include FT_MODULE_H #include FT_LCD_FILTER_H +#include FT_MULTIPLE_MASTERS_H #if defined(FT_CONFIG_OPTIONS_H) #include FT_CONFIG_OPTIONS_H @@ -87,6 +55,10 @@ QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFontMatch) + +using namespace Qt::StringLiterals; + #define FLOOR(x) ((x) & -64) #define CEIL(x) (((x)+63) & -64) #define TRUNC(x) ((x) >> 6) @@ -126,19 +98,43 @@ public: { } ~QtFreetypeData(); + struct FaceStyle { + QString faceFileName; + QString styleName; + + FaceStyle(QString faceFileName, QString styleName) + : faceFileName(std::move(faceFileName)), + styleName(std::move(styleName)) + {} + }; + FT_Library library; QHash<QFontEngine::FaceId, QFreetypeFace *> faces; + QHash<FaceStyle, int> faceIndices; }; QtFreetypeData::~QtFreetypeData() { - for (QHash<QFontEngine::FaceId, QFreetypeFace *>::ConstIterator iter = faces.cbegin(); iter != faces.cend(); ++iter) + for (auto iter = faces.cbegin(); iter != faces.cend(); ++iter) { iter.value()->cleanup(); + if (!iter.value()->ref.deref()) + delete iter.value(); + } faces.clear(); FT_Done_FreeType(library); library = nullptr; } +inline bool operator==(const QtFreetypeData::FaceStyle &style1, const QtFreetypeData::FaceStyle &style2) +{ + return style1.faceFileName == style2.faceFileName && style1.styleName == style2.styleName; +} + +inline size_t qHash(const QtFreetypeData::FaceStyle &style, size_t seed) +{ + return qHashMulti(seed, style.faceFileName, style.styleName); +} + Q_GLOBAL_STATIC(QThreadStorage<QtFreetypeData *>, theFreetypeData) QtFreetypeData *qt_getFreetypeData() @@ -194,10 +190,15 @@ int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QF return Err_Ok; } +bool QFreetypeFace::isScalable() const +{ + return FT_IS_SCALABLE(face); +} + bool QFreetypeFace::isScalableBitmap() const { #ifdef FT_HAS_COLOR - return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face); + return !isScalable() && FT_HAS_COLOR(face); #else return false; #endif @@ -220,18 +221,37 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, QtFreetypeData *freetypeData = qt_getFreetypeData(); - QFreetypeFace *freetype = freetypeData->faces.value(face_id, nullptr); - if (freetype) { - freetype->ref.ref(); - } else { - QScopedPointer<QFreetypeFace> newFreetype(new QFreetypeFace); + QFreetypeFace *freetype = nullptr; + auto it = freetypeData->faces.find(face_id); + if (it != freetypeData->faces.end()) { + freetype = *it; + + Q_ASSERT(freetype->ref.loadRelaxed() > 0); + if (freetype->ref.loadRelaxed() == 1) { + // If there is only one reference left to the face, it means it is only referenced by + // the cache itself, and thus it is in cleanup state (but the final outside reference + // was removed on a different thread so it could not be deleted right away). We then + // complete the cleanup and pretend we didn't find it, so that it can be re-created with + // the present state. + freetype->cleanup(); + freetypeData->faces.erase(it); + delete freetype; + freetype = nullptr; + } else { + freetype->ref.ref(); + } + } + + if (!freetype) { + const auto deleter = [](QFreetypeFace *f) { delete f; }; + std::unique_ptr<QFreetypeFace, decltype(deleter)> newFreetype(new QFreetypeFace, deleter); FT_Face face; if (!face_id.filename.isEmpty()) { QString fileName = QFile::decodeName(face_id.filename); - if (face_id.filename.startsWith(":qmemoryfonts/")) { + if (const char *prefix = ":qmemoryfonts/"; face_id.filename.startsWith(prefix)) { // from qfontdatabase.cpp QByteArray idx = face_id.filename; - idx.remove(0, 14); // remove ':qmemoryfonts/' + idx.remove(0, strlen(prefix)); // remove ':qmemoryfonts/' bool ok = false; newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok)); if (!ok) @@ -253,7 +273,23 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) { return nullptr; } + +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900 + if (face_id.instanceIndex >= 0) { + qCDebug(lcFontMatch) + << "Selecting named instance" << (face_id.instanceIndex) + << "in" << face_id.filename; + FT_Set_Named_Instance(face, face_id.instanceIndex + 1); + } +#endif newFreetype->face = face; + newFreetype->mm_var = nullptr; + if (FT_IS_NAMED_INSTANCE(newFreetype->face)) { + FT_Error ftresult; + ftresult = FT_Get_MM_Var(face, &newFreetype->mm_var); + if (ftresult != FT_Err_Ok) + newFreetype->mm_var = nullptr; + } newFreetype->ref.storeRelaxed(1); newFreetype->xsize = 0; @@ -292,14 +328,34 @@ 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)) { + const auto it = face_id.variableAxes.constFind(*tag); + if (it != face_id.variableAxes.constEnd()) + coords[i] = FT_Fixed(*it * 65536); + } + } + 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.data()); + freetypeData->faces.insert(face_id, newFreetype.get()); } QT_CATCH(...) { - newFreetype.take()->release(face_id); + newFreetype.release()->release(face_id); // we could return null in principle instead of throwing QT_RETHROW; } - freetype = newFreetype.take(); + freetype = newFreetype.release(); + freetype->ref.ref(); } return freetype; } @@ -307,32 +363,93 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, void QFreetypeFace::cleanup() { hbFace.reset(); + if (mm_var && face && face->glyph) + FT_Done_MM_Var(face->glyph->library, mm_var); + mm_var = nullptr; FT_Done_Face(face); face = nullptr; } void QFreetypeFace::release(const QFontEngine::FaceId &face_id) { - if (!ref.deref()) { - if (face) { - QtFreetypeData *freetypeData = qt_getFreetypeData(); + Q_UNUSED(face_id); + bool deleteThis = !ref.deref(); + + // If the only reference left over is the cache's reference, we remove it from the cache, + // granted that we are on the correct thread. If not, we leave it there to be cleaned out + // later. While we are at it, we also purge all left over faces which are only referenced + // from the cache. + if (face && ref.loadRelaxed() == 1) { + QtFreetypeData *freetypeData = qt_getFreetypeData(); + for (auto it = freetypeData->faces.constBegin(); it != freetypeData->faces.constEnd(); ) { + if (it.value()->ref.loadRelaxed() == 1) { + it.value()->cleanup(); + if (it.value() == this) + deleteThis = true; // This face, delete at end of function for safety + else + delete it.value(); + it = freetypeData->faces.erase(it); + } else { + ++it; + } + } - cleanup(); + if (freetypeData->faces.isEmpty()) { + FT_Done_FreeType(freetypeData->library); + freetypeData->library = nullptr; + } + } - auto it = freetypeData->faces.constFind(face_id); - if (it != freetypeData->faces.constEnd()) - freetypeData->faces.erase(it); + if (deleteThis) + delete this; +} - if (freetypeData->faces.isEmpty()) { - FT_Done_FreeType(freetypeData->library); - freetypeData->library = nullptr; - } +static int computeFaceIndex(const QString &faceFileName, const QString &styleName) +{ + FT_Library library = qt_getFreetype(); + + int faceIndex = 0; + int numFaces = 0; + + do { + FT_Face face; + + FT_Error error = FT_New_Face(library, faceFileName.toUtf8().constData(), faceIndex, &face); + if (error != FT_Err_Ok) { + qDebug() << "FT_New_Face failed for face index" << faceIndex << ':' << Qt::hex << error; + break; } - delete this; - } + const bool found = QLatin1StringView(face->style_name) == styleName; + numFaces = face->num_faces; + + FT_Done_Face(face); + + if (found) + return faceIndex; + } while (++faceIndex < numFaces); + + // Fall back to the first font face in the file + return 0; } +int QFreetypeFace::getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName) +{ + QtFreetypeData *freetypeData = qt_getFreetypeData(); + + // Try to get from cache + QtFreetypeData::FaceStyle faceStyle(faceFileName, styleName); + int faceIndex = freetypeData->faceIndices.value(faceStyle, -1); + + if (faceIndex >= 0) + return faceIndex; + + faceIndex = computeFaceIndex(faceFileName, styleName); + + freetypeData->faceIndices.insert(faceStyle, faceIndex); + + return faceIndex; +} void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor) { @@ -594,7 +711,7 @@ static QFontEngine::SubpixelAntialiasingType subpixelAntialiasingTypeHint() QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData) { - QScopedPointer<QFontEngineFT> engine(new QFontEngineFT(fontDef)); + auto engine = std::make_unique<QFontEngineFT>(fontDef); QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono; const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); @@ -616,7 +733,7 @@ QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, con } engine->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference)); - return engine.take(); + return engine.release(); } namespace { @@ -638,27 +755,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; } @@ -711,6 +833,37 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, static void dont_delete(void*) {} +static FT_UShort calculateActualWeight(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId) +{ + FT_MM_Var *var = freetypeFace->mm_var; + if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) { + for (FT_UInt axis = 0; axis < var->num_axis; ++axis) { + if (var->axis[axis].tag == QFont::Tag("wght").value()) { + return var->namedstyle[faceId.instanceIndex].coords[axis] >> 16; + } + } + } + if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) { + return os2->usWeightClass; + } + + return 700; +} + +static bool calculateActualItalic(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId) +{ + FT_MM_Var *var = freetypeFace->mm_var; + if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) { + for (FT_UInt axis = 0; axis < var->num_axis; ++axis) { + if (var->axis[axis].tag == QFont::Tag("ital").value()) { + return (var->namedstyle[faceId.instanceIndex].coords[axis] >> 16) == 1; + } + } + } + + return (face->style_flags & FT_STYLE_FLAG_ITALIC); +} + bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, QFreetypeFace *freetypeFace) { @@ -734,7 +887,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, PS_FontInfoRec psrec; // don't assume that type1 fonts are symbol fonts by default if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) { - symbol = !fontDef.families.isEmpty() && bool(fontDef.families.first().contains(QLatin1String("symbol"), Qt::CaseInsensitive)); + symbol = !fontDef.families.isEmpty() && bool(fontDef.families.constFirst().contains("symbol"_L1, Qt::CaseInsensitive)); } freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor); @@ -742,34 +895,36 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, FT_Face face = lockFace(); if (FT_IS_SCALABLE(face)) { - bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC"); + bool isItalic = calculateActualItalic(freetype, face, faceId); + bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !isItalic && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC"); if (fake_oblique) obliquen = true; FT_Set_Transform(face, &matrix, nullptr); freetype->matrix = matrix; // fake bold if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD")) { - if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) { - if (os2->usWeightClass < 700 && fontDef.pixelSize < 64) - embolden = true; + FT_UShort actualWeight = calculateActualWeight(freetype, face, faceId); + if (actualWeight < 700 && + (fontDef.pixelSize < 64 || qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD_LIMIT"))) { + embolden = true; } } // underline metrics line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)); - underline_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale)); + QFixed center_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale)); + underline_position = center_position - line_thickness / 2; } else { // ad hoc algorithm int score = fontDef.weight * fontDef.pixelSize; - line_thickness = score / 700; + line_thickness = score / 7000; // looks better with thicker line for small pointsizes if (line_thickness < 2 && score >= 1050) line_thickness = 2; underline_position = ((line_thickness * 2) + 3) / 6; - if (isScalableBitmap()) { + cacheEnabled = false; + if (isScalableBitmap()) glyphFormat = defaultFormat = GlyphFormat::Format_ARGB; - cacheEnabled = false; - } } if (line_thickness < 1) line_thickness = 1; @@ -780,7 +935,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, TrueType fonts with embedded bitmaps may have a bitmap font specific ascent/descent in the EBLC table. There is no direct public API to extract those values. The only way we've found is to trick freetype - into thinking that it's not a scalable font in FT_SelectSize so that + into thinking that it's not a scalable font in FT_Select_Size so that the metrics are retrieved from the bitmap strikes. */ if (FT_IS_SCALABLE(face)) { @@ -794,7 +949,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, metrics.ascender = face->size->metrics.ascender; metrics.descender = face->size->metrics.descender; if (metrics.descender > 0 - && QString::fromUtf8(face->family_name) == QLatin1String("Courier New")) { + && QString::fromUtf8(face->family_name) == "Courier New"_L1) { metrics.descender *= -1; } metrics.height = metrics.ascender - metrics.descender + leading; @@ -1072,7 +1227,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } int glyph_buffer_size = 0; - QScopedArrayPointer<uchar> glyph_buffer; + std::unique_ptr<uchar[]> glyph_buffer; FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL; switch (format) { case Format_Mono: @@ -1117,7 +1272,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { uchar *src = slot->bitmap.buffer; - uchar *dst = glyph_buffer.data(); + uchar *dst = glyph_buffer.get(); int h = slot->bitmap.rows; // Some fonts return bitmaps even when we requested something else: if (format == Format_Mono) { @@ -1146,7 +1301,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } else if (slot->bitmap.pixel_mode == 7 /*FT_PIXEL_MODE_BGRA*/) { Q_ASSERT(format == Format_ARGB); uchar *src = slot->bitmap.buffer; - uchar *dst = glyph_buffer.data(); + uchar *dst = glyph_buffer.get(); int h = slot->bitmap.rows; while (h--) { #if Q_BYTE_ORDER == Q_BIG_ENDIAN @@ -1166,7 +1321,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { Q_ASSERT(format == Format_A8); uchar *src = slot->bitmap.buffer; - uchar *dst = glyph_buffer.data(); + uchar *dst = glyph_buffer.get(); int h = slot->bitmap.rows; int bytes = info.width; while (h--) { @@ -1176,10 +1331,10 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { Q_ASSERT(format == Format_A32); - convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB); + convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB); } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) { Q_ASSERT(format == Format_A32); - convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB); + convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB); } else { qWarning("QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode); return nullptr; @@ -1198,7 +1353,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, g->advance = info.xOff; g->format = format; delete [] g->data; - g->data = glyph_buffer.take(); + g->data = glyph_buffer.release(); if (set) set->setGlyph(glyph, subPixelPosition, g); @@ -1215,7 +1370,7 @@ QFontEngine::Properties QFontEngineFT::properties() const { Properties p = freetype->properties(); if (p.postscriptName.isEmpty()) { - p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.families.first().toUtf8()); + p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.families.constFirst().toUtf8()); } return freetype->properties(); @@ -1442,7 +1597,7 @@ void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_me bool QFontEngineFT::supportsTransformation(const QTransform &transform) const { - return transform.type() <= QTransform::TxRotate; + return transform.type() <= QTransform::TxRotate && (freetype->isScalable() || freetype->isScalableBitmap()); } void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) @@ -1523,15 +1678,16 @@ glyph_t QFontEngineFT::glyphIndex(uint ucs4) const return glyph; } -bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, +int QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const { Q_ASSERT(glyphs->numGlyphs >= *nglyphs); if (*nglyphs < len) { *nglyphs = len; - return false; + return -1; } + int mappedGlyphs = 0; int glyph_pos = 0; if (freetype->symbol_map) { FT_Face face = freetype->face; @@ -1564,6 +1720,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs if (uc < QFreetypeFace::cmapCacheSize) freetype->cmapCache[uc] = glyph; } + if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc)) + mappedGlyphs++; ++glyph_pos; } } else { @@ -1585,6 +1743,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs freetype->cmapCache[uc] = glyph; } } + if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc)) + mappedGlyphs++; ++glyph_pos; } } @@ -1595,7 +1755,7 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs if (!(flags & GlyphIndicesOnly)) recalcAdvances(glyphs, flags); - return true; + return mappedGlyphs; } bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const @@ -1705,9 +1865,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) QFixed y = overall.yoff + glyphs.offsets[i].y - g->y; overall.x = qMin(overall.x, x); overall.y = qMin(overall.y, y); - xmax = qMax(xmax, x + g->width); - ymax = qMax(ymax, y + g->height); - overall.xoff += g->advance; + xmax = qMax(xmax, x.ceil() + g->width); + ymax = qMax(ymax, y.ceil() + g->height); if (!cacheEnabled && g != &emptyGlyph) delete g; } else { @@ -1722,8 +1881,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) overall.y = qMin(overall.y, y); xmax = qMax(xmax, x + TRUNC(right - left)); ymax = qMax(ymax, y + TRUNC(top - bottom)); - overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x))); } + overall.xoff += glyphs.effectiveAdvance(i); } overall.height = qMax(overall.height, ymax - overall.y); overall.width = xmax - overall.x; @@ -1787,7 +1946,15 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, const QTransform &matrix, QFontEngine::GlyphFormat format) { - Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true); + // When rendering glyphs into a cache via the alphaMap* functions, we disable + // outline drawing. To ensure the bounding box matches the rendered glyph, we + // need to do the same here. + + const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face) + && matrix.type() > QTransform::TxTranslate; + if (needsImageTransform && format == QFontEngine::Format_Mono) + format = QFontEngine::Format_A8; + Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true, true); glyph_metrics_t overall; if (g) { @@ -1813,7 +1980,7 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, unlockFace(); } - if (isScalableBitmap()) + if (isScalableBitmap() || needsImageTransform) overall = scaledBitmapMetrics(overall, matrix); return overall; } @@ -1913,12 +2080,17 @@ QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, const QFixedPoint &subPixelPosition, const QTransform &t) { - const GlyphFormat neededFormat = antialias ? Format_A8 : Format_Mono; + const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face) + && t.type() > QTransform::TxTranslate; + const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono; Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); QImage img = alphaMapFromGlyphData(glyph, neededFormat); - img = img.copy(); + if (needsImageTransform) + img = img.transformed(t, Qt::SmoothTransformation); + else + img = img.copy(); if (!cacheEnabled && glyph != &emptyGlyph) delete glyph; diff --git a/src/gui/text/freetype/qfontengine_ft_p.h b/src/gui/text/freetype/qfontengine_ft_p.h index 03059c7d13..bdd4549827 100644 --- a/src/gui/text/freetype/qfontengine_ft_p.h +++ b/src/gui/text/freetype/qfontengine_ft_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QFONTENGINE_FT_P_H #define QFONTENGINE_FT_P_H // @@ -55,6 +19,7 @@ #include <ft2build.h> #include FT_FREETYPE_H +#include FT_MULTIPLE_MASTERS_H #ifndef Q_OS_WIN @@ -85,6 +50,8 @@ public: const QByteArray &fontData = QByteArray()); void release(const QFontEngine::FaceId &face_id); + static int getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName); + // locks the struct for usage. Any read/write operations require locking. void lock() { @@ -96,6 +63,7 @@ public: } FT_Face face; + FT_MM_Var *mm_var; int xsize; // 26.6 int ysize; // 26.6 FT_Matrix matrix; @@ -109,6 +77,7 @@ public: int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints); + bool isScalable() const; bool isScalableBitmap() const; static void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale); @@ -117,7 +86,6 @@ public: private: friend class QFontEngineFT; friend class QtFreetypeData; - friend struct QScopedPointerDeleter<QFreetypeFace>; QFreetypeFace() = default; ~QFreetypeFace() {} void cleanup(); @@ -218,7 +186,7 @@ private: void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) override; - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; + int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override; glyph_metrics_t boundingBox(glyph_t glyph) override; @@ -301,7 +269,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: @@ -360,8 +328,6 @@ private: QFixed scalableBitmapScaleFactor; }; -Q_DECLARE_TYPEINFO(QFontEngineFT::QGlyphSet, Q_RELOCATABLE_TYPE); - inline size_t qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g, size_t seed = 0) { diff --git a/src/gui/text/freetype/qfreetypefontdatabase.cpp b/src/gui/text/freetype/qfreetypefontdatabase.cpp index ac7520c495..018e590ac2 100644 --- a/src/gui/text/freetype/qfreetypefontdatabase.cpp +++ b/src/gui/text/freetype/qfreetypefontdatabase.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qfreetypefontdatabase_p.h" @@ -46,6 +10,8 @@ #include <QtCore/QLibraryInfo> #include <QtCore/QDir> #include <QtCore/QtEndian> +#include <QtCore/QLoggingCategory> +#include <QtCore/QUuid> #undef QT_NO_FREETYPE #include "qfontengine_ft_p.h" @@ -54,8 +20,16 @@ #include FT_TRUETYPE_TABLES_H #include FT_ERRORS_H +#include FT_MULTIPLE_MASTERS_H +#include FT_SFNT_NAMES_H +#include FT_TRUETYPE_IDS_H + QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFontDb) + +using namespace Qt::StringLiterals; + void QFreeTypeFontDatabase::populateFontDatabase() { QString fontpath = fontDir(); @@ -68,14 +42,14 @@ void QFreeTypeFontDatabase::populateFontDatabase() return; } - QStringList nameFilters; - nameFilters << QLatin1String("*.ttf") - << QLatin1String("*.ttc") - << QLatin1String("*.pfa") - << QLatin1String("*.pfb") - << QLatin1String("*.otf"); + static const QString nameFilters[] = { + u"*.ttf"_s, + u"*.pfa"_s, + u"*.pfb"_s, + u"*.otf"_s, + }; - const auto fis = dir.entryInfoList(nameFilters, QDir::Files); + const auto fis = dir.entryInfoList(QStringList::fromReadOnlyData(nameFilters), QDir::Files); for (const QFileInfo &fi : fis) { const QByteArray file = QFile::encodeName(fi.absoluteFilePath()); QFreeTypeFontDatabase::addTTFile(QByteArray(), file); @@ -88,14 +62,24 @@ QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *us QFontEngine::FaceId faceId; faceId.filename = QFile::encodeName(fontfile->fileName); faceId.index = fontfile->indexValue; + faceId.instanceIndex = fontfile->instanceIndex; + faceId.variableAxes = fontDef.variableAxisValues; + + // Make sure the FaceId compares uniquely in cases where a + // file name is not provided. + if (faceId.filename.isEmpty()) { + QUuid::Id128Bytes id{}; + memcpy(&id, &usrPtr, sizeof(usrPtr)); + faceId.uuid = QUuid(id).toByteArray(); + } - return QFontEngineFT::create(fontDef, faceId); + return QFontEngineFT::create(fontDef, faceId, fontfile->data); } 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) @@ -111,6 +95,114 @@ void QFreeTypeFontDatabase::releaseHandle(void *handle) extern FT_Library qt_getFreetype(); +void QFreeTypeFontDatabase::addNamedInstancesForFace(void *face_, + int faceIndex, + const QString &family, + const QString &styleName, + QFont::Weight weight, + QFont::Stretch stretch, + QFont::Style style, + bool fixedPitch, + const QSupportedWritingSystems &writingSystems, + const QByteArray &fileName, + const QByteArray &fontData) +{ + FT_Face face = reinterpret_cast<FT_Face>(face_); + + // Note: The following does not actually depend on API from 2.9, but the + // FT_Set_Named_Instance() was added in 2.9, so to avoid populating the database with + // named instances that cannot be selected, we disable the feature on older Freetype + // versions. +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900 + FT_MM_Var *var = nullptr; + FT_Get_MM_Var(face, &var); + if (var != nullptr) { + std::unique_ptr<FT_MM_Var, void(*)(FT_MM_Var*)> varGuard(var, [](FT_MM_Var *res) { + FT_Done_MM_Var(qt_getFreetype(), res); + }); + + for (FT_UInt i = 0; i < var->num_namedstyles; ++i) { + FT_UInt id = var->namedstyle[i].strid; + + QFont::Weight instanceWeight = weight; + QFont::Stretch instanceStretch = stretch; + QFont::Style instanceStyle = style; + for (FT_UInt axis = 0; axis < var->num_axis; ++axis) { + if (var->axis[axis].tag == QFont::Tag("wght").value()) { + instanceWeight = QFont::Weight(var->namedstyle[i].coords[axis] >> 16); + } else if (var->axis[axis].tag == QFont::Tag("wdth").value()) { + instanceStretch = QFont::Stretch(var->namedstyle[i].coords[axis] >> 16); + } else if (var->axis[axis].tag == QFont::Tag("ital").value()) { + FT_UInt ital = var->namedstyle[i].coords[axis] >> 16; + if (ital == 1) + instanceStyle = QFont::StyleItalic; + else + instanceStyle = QFont::StyleNormal; + } + } + + FT_UInt count = FT_Get_Sfnt_Name_Count(face); + for (FT_UInt j = 0; j < count; ++j) { + FT_SfntName name; + if (FT_Get_Sfnt_Name(face, j, &name)) + continue; + + if (name.name_id != id) + continue; + + // Only support Unicode for now + if (name.encoding_id != TT_MS_ID_UNICODE_CS) + continue; + + // Sfnt names stored as UTF-16BE + QString instanceName; + for (FT_UInt k = 0; k < name.string_len; k += 2) + instanceName += QChar((name.string[k] << 8) + name.string[k + 1]); + if (instanceName != styleName) { + FontFile *variantFontFile = new FontFile{ + QFile::decodeName(fileName), + faceIndex, + int(i), + fontData + }; + + qCDebug(lcFontDb) << "Registering named instance" << i + << ":" << instanceName + << "for font family" << family + << "with weight" << instanceWeight + << ", style" << instanceStyle + << ", stretch" << instanceStretch; + + registerFont(family, + instanceName, + QString(), + instanceWeight, + instanceStyle, + instanceStretch, + true, + true, + 0, + fixedPitch, + writingSystems, + variantFontFile); + } + } + } + } +#else + Q_UNUSED(face); + Q_UNUSED(family); + Q_UNUSED(styleName); + Q_UNUSED(weight); + Q_UNUSED(stretch); + Q_UNUSED(style); + Q_UNUSED(fixedPitch); + Q_UNUSED(writingSystems); + Q_UNUSED(fontData); +#endif + +} + QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file, QFontDatabasePrivate::ApplicationFont *applicationFont) { FT_Library library = qt_getFreetype(); @@ -153,6 +245,7 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q } } + QFont::Stretch stretch = QFont::Unstretched; TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); if (os2) { quint32 unicodeRange[4] = { @@ -191,14 +284,46 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q else if (w <= 10) weight = QFont::Black; } + + switch (os2->usWidthClass) { + case 1: + stretch = QFont::UltraCondensed; + break; + case 2: + stretch = QFont::ExtraCondensed; + break; + case 3: + stretch = QFont::Condensed; + break; + case 4: + stretch = QFont::SemiCondensed; + break; + case 5: + stretch = QFont::Unstretched; + break; + case 6: + stretch = QFont::SemiExpanded; + break; + case 7: + stretch = QFont::Expanded; + break; + case 8: + stretch = QFont::ExtraExpanded; + break; + case 9: + stretch = QFont::UltraExpanded; + break; + } } QString family = QString::fromLatin1(face->family_name); - FontFile *fontFile = new FontFile; - fontFile->fileName = QFile::decodeName(file); - fontFile->indexValue = index; + FontFile *fontFile = new FontFile{ + QFile::decodeName(file), + index, + -1, + fontData + }; - QFont::Stretch stretch = QFont::Unstretched; QString styleName = QString::fromLatin1(face->style_name); if (applicationFont != nullptr) { @@ -213,6 +338,9 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q } registerFont(family, styleName, QString(), weight, style, stretch, true, true, 0, fixedPitch, writingSystems, fontFile); + + addNamedInstancesForFace(face, index, family, styleName, weight, stretch, style, fixedPitch, writingSystems, file, fontData); + families.append(family); FT_Done_Face(face); @@ -221,4 +349,13 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q return families; } +bool QFreeTypeFontDatabase::supportsVariableApplicationFonts() const +{ +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900 + return true; +#else + return false; +#endif +} + QT_END_NAMESPACE diff --git a/src/gui/text/freetype/qfreetypefontdatabase_p.h b/src/gui/text/freetype/qfreetypefontdatabase_p.h index 2cc725bd06..5fcec585d2 100644 --- a/src/gui/text/freetype/qfreetypefontdatabase_p.h +++ b/src/gui/text/freetype/qfreetypefontdatabase_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QFREETYPEFONTDATABASE_H #define QFREETYPEFONTDATABASE_H @@ -54,6 +18,7 @@ #include <qpa/qplatformfontdatabase.h> #include <QtCore/QByteArray> #include <QtCore/QString> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -61,6 +26,12 @@ struct FontFile { QString fileName; int indexValue; + int instanceIndex = -1; + + // Note: The data may be implicitly shared throughout the + // font database and platform font database, so be careful + // to never detach when accessing this member! + const QByteArray data; }; class Q_GUI_EXPORT QFreeTypeFontDatabase : public QPlatformFontDatabase @@ -71,6 +42,14 @@ public: QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override; QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr) override; void releaseHandle(void *handle) override; + bool supportsVariableApplicationFonts() const override; + + static void addNamedInstancesForFace(void *face, int faceIndex, + const QString &family, const QString &styleName, + QFont::Weight weight, QFont::Stretch stretch, + QFont::Style style, bool fixedPitch, + const QSupportedWritingSystems &writingSystems, + const QByteArray &fileName, const QByteArray &fontData); static QStringList addTTFile(const QByteArray &fontData, const QByteArray &file, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr); }; |