FreeType: Load multiple font faces from the same file on macOS

Previously, if a font file contained multiple font faces, only the first
could ever be loaded. This could lead to Qt loading different font
styles than requested by the application.

[ChangeLog][Text][Freetype] Fixed a bug where the macOS FreeType backend
would fail to load font faces from font files containing multiple faces.

Fixes: QTBUG-100666
Pick-to: 6.2 6.3
Change-Id: I6a126266a2e15f843dd578ab25c11748881bb932
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
Niklas Wenzel 2022-02-10 09:58:11 +01:00
parent d34ae86a00
commit af875e88f4
3 changed files with 77 additions and 2 deletions

View File

@ -403,9 +403,15 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const
return QFontEngineFT::create(*fontData, fontDef.pixelSize,
static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
} else if (NSURL *url = descriptorAttribute<NSURL>(descriptor, kCTFontURLAttribute)) {
Q_ASSERT(url.fileURL);
QFontEngine::FaceId faceId;
faceId.filename = QString::fromNSString(url.path).toUtf8();
Q_ASSERT(url.fileURL);
QString faceFileName{QString::fromNSString(url.path)};
faceId.filename = faceFileName.toUtf8();
QString styleName = QCFString(CTFontDescriptorCopyAttribute(descriptor, kCTFontStyleNameAttribute));
faceId.index = QFreetypeFace::getFaceIndexByStyleName(faceFileName, styleName);
return QFontEngineFT::create(fontDef, faceId);
}
// We end up here with a descriptor does not contain Qt font data or kCTFontURLAttribute.

View File

@ -128,8 +128,19 @@ 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()
@ -141,6 +152,16 @@ QtFreetypeData::~QtFreetypeData()
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()
@ -336,6 +357,52 @@ void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
}
}
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;
}
QString faceStyleName = QString::fromLatin1(face->style_name);
numFaces = face->num_faces;
FT_Done_Face(face);
if (faceStyleName == styleName)
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)
{

View File

@ -85,6 +85,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()
{