From e33b0118b402224e75cb7bd6468d719312505b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 24 Mar 2017 14:29:03 +0100 Subject: Rename QBasicFontDatabase to QFreeTypeFontDatabase which is what it is Change-Id: I8def2f7ae1e4c8d8a3e1f8e60549da5d691e4fb3 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/platformsupport/fontdatabases/basic/basic.pri | 9 - .../fontdatabases/basic/qbasicfontdatabase.cpp | 282 --- .../fontdatabases/basic/qbasicfontdatabase_p.h | 80 - .../fontdatabases/basic/qfontengine_ft.cpp | 2214 -------------------- .../fontdatabases/basic/qfontengine_ft_p.h | 360 ---- .../fontconfig/qfontconfigdatabase.cpp | 4 +- .../fontconfig/qfontconfigdatabase_p.h | 4 +- .../fontdatabases/fontdatabases.pro | 2 +- .../fontdatabases/freetype/freetype.pri | 9 + .../fontdatabases/freetype/qfontengine_ft.cpp | 2214 ++++++++++++++++++++ .../fontdatabases/freetype/qfontengine_ft_p.h | 360 ++++ .../freetype/qfreetypefontdatabase.cpp | 282 +++ .../freetype/qfreetypefontdatabase_p.h | 80 + .../genericunix/qgenericunixfontdatabase_p.h | 4 +- src/platformsupport/fontdatabases/mac/coretext.pri | 4 +- .../windows/qwindowsfontdatabase_ft.cpp | 6 +- .../windows/qwindowsfontdatabase_ft_p.h | 4 +- .../fontdatabases/winrt/qwinrtfontdatabase.cpp | 14 +- .../fontdatabases/winrt/qwinrtfontdatabase_p.h | 4 +- 19 files changed, 2968 insertions(+), 2968 deletions(-) delete mode 100644 src/platformsupport/fontdatabases/basic/basic.pri delete mode 100644 src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp delete mode 100644 src/platformsupport/fontdatabases/basic/qbasicfontdatabase_p.h delete mode 100644 src/platformsupport/fontdatabases/basic/qfontengine_ft.cpp delete mode 100644 src/platformsupport/fontdatabases/basic/qfontengine_ft_p.h create mode 100644 src/platformsupport/fontdatabases/freetype/freetype.pri create mode 100644 src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp create mode 100644 src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h create mode 100644 src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp create mode 100644 src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase_p.h (limited to 'src/platformsupport/fontdatabases') diff --git a/src/platformsupport/fontdatabases/basic/basic.pri b/src/platformsupport/fontdatabases/basic/basic.pri deleted file mode 100644 index 0617bf74d7..0000000000 --- a/src/platformsupport/fontdatabases/basic/basic.pri +++ /dev/null @@ -1,9 +0,0 @@ -HEADERS += \ - $$PWD/qbasicfontdatabase_p.h \ - $$PWD/qfontengine_ft_p.h - -SOURCES += \ - $$PWD/qbasicfontdatabase.cpp \ - $$PWD/qfontengine_ft.cpp - -QMAKE_USE_PRIVATE += freetype diff --git a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp deleted file mode 100644 index 0826b0f2fb..0000000000 --- a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "qbasicfontdatabase_p.h" - -#include -#include - -#include -#include -#include -#include -#include - -#undef QT_NO_FREETYPE -#include - -#include -#include FT_TRUETYPE_TABLES_H -#include FT_ERRORS_H - -QT_BEGIN_NAMESPACE - -void QBasicFontDatabase::populateFontDatabase() -{ - QString fontpath = fontDir(); - QDir dir(fontpath); - - if (!dir.exists()) { - qWarning("QFontDatabase: Cannot find font directory %s.\n" - "Note that Qt no longer ships fonts. Deploy some (from http://dejavu-fonts.org for example) or switch to fontconfig.", - qPrintable(fontpath)); - return; - } - - QStringList nameFilters; - nameFilters << QLatin1String("*.ttf") - << QLatin1String("*.ttc") - << QLatin1String("*.pfa") - << QLatin1String("*.pfb") - << QLatin1String("*.otf"); - - const auto fis = dir.entryInfoList(nameFilters, QDir::Files); - for (const QFileInfo &fi : fis) { - const QByteArray file = QFile::encodeName(fi.absoluteFilePath()); - QBasicFontDatabase::addTTFile(QByteArray(), file); - } -} - -QFontEngine *QBasicFontDatabase::fontEngine(const QFontDef &fontDef, void *usrPtr) -{ - FontFile *fontfile = static_cast (usrPtr); - QFontEngine::FaceId fid; - fid.filename = QFile::encodeName(fontfile->fileName); - fid.index = fontfile->indexValue; - - bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); - QFontEngineFT *engine = new QFontEngineFT(fontDef); - QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono; - if (antialias) { - QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint(); - if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) { - format = QFontEngineFT::Format_A8; - engine->subpixelType = QFontEngine::Subpixel_None; - } else { - format = QFontEngineFT::Format_A32; - engine->subpixelType = subpixelType; - } - } - - if (!engine->init(fid, antialias, format) || engine->invalid()) { - delete engine; - engine = 0; - } else { - engine->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); - } - - return engine; -} - -namespace { - - class QFontEngineFTRawData: public QFontEngineFT - { - public: - QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef) - { - } - - void updateFamilyNameAndStyle() - { - fontDef.family = QString::fromLatin1(freetype->face->family_name); - - if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC) - fontDef.style = QFont::StyleItalic; - - if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD) - fontDef.weight = QFont::Bold; - } - - bool initFromData(const QByteArray &fontData) - { - FaceId faceId; - faceId.filename = ""; - faceId.index = 0; - faceId.uuid = QUuid::createUuid().toByteArray(); - - return init(faceId, true, Format_None, fontData); - } - }; - -} - -QFontEngine *QBasicFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, - QFont::HintingPreference hintingPreference) -{ - QFontDef fontDef; - fontDef.pixelSize = pixelSize; - fontDef.hintingPreference = hintingPreference; - - QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef); - if (!fe->initFromData(fontData)) { - delete fe; - return 0; - } - - fe->updateFamilyNameAndStyle(); - fe->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); - - return fe; -} - -QStringList QBasicFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) -{ - return QBasicFontDatabase::addTTFile(fontData, fileName.toLocal8Bit()); -} - -void QBasicFontDatabase::releaseHandle(void *handle) -{ - FontFile *file = static_cast(handle); - delete file; -} - -extern FT_Library qt_getFreetype(); - -QStringList QBasicFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file) -{ - FT_Library library = qt_getFreetype(); - - int index = 0; - int numFaces = 0; - QStringList families; - do { - FT_Face face; - FT_Error error; - if (!fontData.isEmpty()) { - error = FT_New_Memory_Face(library, (const FT_Byte *)fontData.constData(), fontData.size(), index, &face); - } else { - error = FT_New_Face(library, file.constData(), index, &face); - } - if (error != FT_Err_Ok) { - qDebug() << "FT_New_Face failed with index" << index << ':' << hex << error; - break; - } - numFaces = face->num_faces; - - QFont::Weight weight = QFont::Normal; - - QFont::Style style = QFont::StyleNormal; - if (face->style_flags & FT_STYLE_FLAG_ITALIC) - style = QFont::StyleItalic; - - if (face->style_flags & FT_STYLE_FLAG_BOLD) - weight = QFont::Bold; - - bool fixedPitch = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH); - - QSupportedWritingSystems writingSystems; - // detect symbol fonts - for (int i = 0; i < face->num_charmaps; ++i) { - FT_CharMap cm = face->charmaps[i]; - if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM - || cm->encoding == FT_ENCODING_MS_SYMBOL) { - writingSystems.setSupported(QFontDatabase::Symbol); - break; - } - } - - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); - if (os2) { - quint32 unicodeRange[4] = { - quint32(os2->ulUnicodeRange1), - quint32(os2->ulUnicodeRange2), - quint32(os2->ulUnicodeRange3), - quint32(os2->ulUnicodeRange4) - }; - quint32 codePageRange[2] = { - quint32(os2->ulCodePageRange1), - quint32(os2->ulCodePageRange2) - }; - - writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); - - if (os2->usWeightClass) { - weight = QPlatformFontDatabase::weightFromInteger(os2->usWeightClass); - } else if (os2->panose[2]) { - int w = os2->panose[2]; - if (w <= 1) - weight = QFont::Thin; - else if (w <= 2) - weight = QFont::ExtraLight; - else if (w <= 3) - weight = QFont::Light; - else if (w <= 5) - weight = QFont::Normal; - else if (w <= 6) - weight = QFont::Medium; - else if (w <= 7) - weight = QFont::DemiBold; - else if (w <= 8) - weight = QFont::Bold; - else if (w <= 9) - weight = QFont::ExtraBold; - else if (w <= 10) - weight = QFont::Black; - } - } - - QString family = QString::fromLatin1(face->family_name); - FontFile *fontFile = new FontFile; - fontFile->fileName = QFile::decodeName(file); - fontFile->indexValue = index; - - QFont::Stretch stretch = QFont::Unstretched; - - registerFont(family,QString::fromLatin1(face->style_name),QString(),weight,style,stretch,true,true,0,fixedPitch,writingSystems,fontFile); - - families.append(family); - - FT_Done_Face(face); - ++index; - } while (index < numFaces); - return families; -} - -QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase_p.h b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase_p.h deleted file mode 100644 index 8d8f61973b..0000000000 --- a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase_p.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QBASICFONTDATABASE_H -#define QBASICFONTDATABASE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -struct FontFile -{ - QString fileName; - int indexValue; -}; - -class QBasicFontDatabase : public QPlatformFontDatabase -{ -public: - void populateFontDatabase() Q_DECL_OVERRIDE; - QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE; - QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) Q_DECL_OVERRIDE; - QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName) Q_DECL_OVERRIDE; - void releaseHandle(void *handle) Q_DECL_OVERRIDE; - - static QStringList addTTFile(const QByteArray &fontData, const QByteArray &file); -}; - -QT_END_NAMESPACE - -#endif // QBASICFONTDATABASE_H diff --git a/src/platformsupport/fontdatabases/basic/qfontengine_ft.cpp b/src/platformsupport/fontdatabases/basic/qfontengine_ft.cpp deleted file mode 100644 index 03e72546eb..0000000000 --- a/src/platformsupport/fontdatabases/basic/qfontengine_ft.cpp +++ /dev/null @@ -1,2214 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ - -#include "qdir.h" -#include "qmetatype.h" -#include "qtextstream.h" -#include "qvariant.h" -#include "qfontengine_ft_p.h" -#include "private/qimage_p.h" -#include - -#ifndef QT_NO_FREETYPE - -#include "qfile.h" -#include "qfileinfo.h" -#include -#include "qthreadstorage.h" -#include -#include - -#include -#include FT_FREETYPE_H -#include FT_OUTLINE_H -#include FT_SYNTHESIS_H -#include FT_TRUETYPE_TABLES_H -#include FT_TYPE1_TABLES_H -#include FT_GLYPH_H -#include FT_MODULE_H - -#if defined(FT_LCD_FILTER_H) -#include FT_LCD_FILTER_H -#define QT_USE_FREETYPE_LCDFILTER -#endif - -#if defined(FT_CONFIG_OPTIONS_H) -#include FT_CONFIG_OPTIONS_H -#endif - -#if defined(FT_FONT_FORMATS_H) -#include FT_FONT_FORMATS_H -#endif - -#ifdef QT_LINUXBASE -#include FT_ERRORS_H -#endif - -#if !defined(QT_MAX_CACHED_GLYPH_SIZE) -# define QT_MAX_CACHED_GLYPH_SIZE 64 -#endif - -QT_BEGIN_NAMESPACE - -#define FLOOR(x) ((x) & -64) -#define CEIL(x) (((x)+63) & -64) -#define TRUNC(x) ((x) >> 6) -#define ROUND(x) (((x)+32) & -64) - -static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length) -{ - FT_Face face = (FT_Face)user_data; - - bool result = false; - if (FT_IS_SFNT(face)) { - FT_ULong len = *length; - result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok; - *length = len; - Q_ASSERT(!result || int(*length) > 0); - } - - return result; -} - -static QFontEngineFT::Glyph emptyGlyph = {0, 0, 0, 0, 0, 0, 0, 0}; - -static const QFontEngine::HintStyle ftInitialDefaultHintStyle = -#ifdef Q_OS_WIN - QFontEngineFT::HintFull; -#else - QFontEngineFT::HintNone; -#endif - -// -------------------------- Freetype support ------------------------------ - -class QtFreetypeData -{ -public: - QtFreetypeData() - : library(0) - { } - ~QtFreetypeData(); - - FT_Library library; - QHash faces; -}; - -QtFreetypeData::~QtFreetypeData() -{ - for (QHash::ConstIterator iter = faces.cbegin(); iter != faces.cend(); ++iter) - iter.value()->cleanup(); - faces.clear(); - FT_Done_FreeType(library); - library = 0; -} - -#ifdef QT_NO_THREAD -Q_GLOBAL_STATIC(QtFreetypeData, theFreetypeData) - -QtFreetypeData *qt_getFreetypeData() -{ - return theFreetypeData(); -} -#else -Q_GLOBAL_STATIC(QThreadStorage, theFreetypeData) - -QtFreetypeData *qt_getFreetypeData() -{ - QtFreetypeData *&freetypeData = theFreetypeData()->localData(); - if (!freetypeData) - freetypeData = new QtFreetypeData; - if (!freetypeData->library) { - FT_Init_FreeType(&freetypeData->library); -#if defined(FT_FONT_FORMATS_H) - // Freetype defaults to disabling stem-darkening on CFF, we re-enable it. - FT_Bool no_darkening = false; - FT_Property_Set(freetypeData->library, "cff", "no-stem-darkening", &no_darkening); -#endif - } - return freetypeData; -} -#endif - -FT_Library qt_getFreetype() -{ - QtFreetypeData *freetypeData = qt_getFreetypeData(); - Q_ASSERT(freetypeData->library); - return freetypeData->library; -} - -int QFreetypeFace::fsType() const -{ - int fsType = 0; - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); - if (os2) - fsType = os2->fsType; - return fsType; -} - -int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) -{ - if (int error = FT_Load_Glyph(face, glyph, flags)) - return error; - - if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) - return Err_Invalid_SubTable; - - *nPoints = face->glyph->outline.n_points; - if (!(*nPoints)) - return Err_Ok; - - if (point > *nPoints) - return Err_Invalid_SubTable; - - *xpos = QFixed::fromFixed(face->glyph->outline.points[point].x); - *ypos = QFixed::fromFixed(face->glyph->outline.points[point].y); - - return Err_Ok; -} - -bool QFreetypeFace::isScalableBitmap() const -{ -#ifdef FT_HAS_COLOR - return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face); -#else - return false; -#endif -} - -extern QByteArray qt_fontdata_from_index(int); - -/* - * One font file can contain more than one font (bold/italic for example) - * find the right one and return it. - * - * Returns the freetype face or 0 in case of an empty file or any other problems - * (like not being able to open the file) - */ -QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, - const QByteArray &fontData) -{ - if (face_id.filename.isEmpty() && fontData.isEmpty()) - return 0; - - QtFreetypeData *freetypeData = qt_getFreetypeData(); - - QFreetypeFace *freetype = freetypeData->faces.value(face_id, 0); - if (freetype) { - freetype->ref.ref(); - } else { - QScopedPointer newFreetype(new QFreetypeFace); - FT_Face face; - if (!face_id.filename.isEmpty()) { - QString fileName = QFile::decodeName(face_id.filename); - if (face_id.filename.startsWith(":qmemoryfonts/")) { - // from qfontdatabase.cpp - QByteArray idx = face_id.filename; - idx.remove(0, 14); // remove ':qmemoryfonts/' - bool ok = false; - newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok)); - if (!ok) - newFreetype->fontData = QByteArray(); - } else if (!QFileInfo(fileName).isNativePath()) { - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) { - return 0; - } - newFreetype->fontData = file.readAll(); - } - } else { - newFreetype->fontData = fontData; - } - if (!newFreetype->fontData.isEmpty()) { - if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) { - return 0; - } - } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) { - return 0; - } - newFreetype->face = face; - - newFreetype->ref.store(1); - newFreetype->xsize = 0; - newFreetype->ysize = 0; - newFreetype->matrix.xx = 0x10000; - newFreetype->matrix.yy = 0x10000; - newFreetype->matrix.xy = 0; - newFreetype->matrix.yx = 0; - newFreetype->unicode_map = 0; - newFreetype->symbol_map = 0; - - memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache)); - - for (int i = 0; i < newFreetype->face->num_charmaps; ++i) { - FT_CharMap cm = newFreetype->face->charmaps[i]; - switch(cm->encoding) { - case FT_ENCODING_UNICODE: - newFreetype->unicode_map = cm; - break; - case FT_ENCODING_APPLE_ROMAN: - case FT_ENCODING_ADOBE_LATIN_1: - if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE) - newFreetype->unicode_map = cm; - break; - case FT_ENCODING_ADOBE_CUSTOM: - case FT_ENCODING_MS_SYMBOL: - if (!newFreetype->symbol_map) - newFreetype->symbol_map = cm; - break; - default: - break; - } - } - - if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1) - 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); - QT_TRY { - freetypeData->faces.insert(face_id, newFreetype.data()); - } QT_CATCH(...) { - newFreetype.take()->release(face_id); - // we could return null in principle instead of throwing - QT_RETHROW; - } - freetype = newFreetype.take(); - } - return freetype; -} - -void QFreetypeFace::cleanup() -{ - hbFace.reset(); - FT_Done_Face(face); - face = 0; -} - -void QFreetypeFace::release(const QFontEngine::FaceId &face_id) -{ - if (!ref.deref()) { - if (face) { - QtFreetypeData *freetypeData = qt_getFreetypeData(); - - cleanup(); - - auto it = freetypeData->faces.constFind(face_id); - if (it != freetypeData->faces.constEnd()) - freetypeData->faces.erase(it); - - if (freetypeData->faces.isEmpty()) { - FT_Done_FreeType(freetypeData->library); - freetypeData->library = 0; - } - } - - delete this; - } -} - - -void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor) -{ - *ysize = qRound(fontDef.pixelSize * 64); - *xsize = *ysize * fontDef.stretch / 100; - *scalableBitmapScaleFactor = 1; - *outline_drawing = false; - - if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) { - int best = 0; - if (!isScalableBitmap()) { - /* - * Bitmap only faces must match exactly, so find the closest - * one (height dominant search) - */ - for (int i = 1; i < face->num_fixed_sizes; i++) { - if (qAbs(*ysize - face->available_sizes[i].y_ppem) < - qAbs(*ysize - face->available_sizes[best].y_ppem) || - (qAbs(*ysize - face->available_sizes[i].y_ppem) == - qAbs(*ysize - face->available_sizes[best].y_ppem) && - qAbs(*xsize - face->available_sizes[i].x_ppem) < - qAbs(*xsize - face->available_sizes[best].x_ppem))) { - best = i; - } - } - } else { - // Select the shortest bitmap strike whose height is larger than the desired height - for (int i = 1; i < face->num_fixed_sizes; i++) { - if (face->available_sizes[i].y_ppem < *ysize) { - if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem) - best = i; - } else if (face->available_sizes[best].y_ppem < *ysize) { - best = i; - } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) { - best = i; - } - } - } - - // According to freetype documentation we must use FT_Select_Size - // to make sure we can select the desired bitmap strike index - if (FT_Select_Size(face, best) == 0) { - if (isScalableBitmap()) - *scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / face->available_sizes[best].height); - *xsize = face->available_sizes[best].x_ppem; - *ysize = face->available_sizes[best].y_ppem; - } else { - *xsize = *ysize = 0; - } - } else { - *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6)); - } -} - -QFontEngine::Properties QFreetypeFace::properties() const -{ - QFontEngine::Properties p; - p.postscriptName = FT_Get_Postscript_Name(face); - PS_FontInfoRec font_info; - if (FT_Get_PS_Font_Info(face, &font_info) == 0) - p.copyright = font_info.notice; - if (FT_IS_SCALABLE(face)) { - p.ascent = face->ascender; - p.descent = -face->descender; - p.leading = face->height - face->ascender + face->descender; - p.emSquare = face->units_per_EM; - p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax, - face->bbox.xMax - face->bbox.xMin, - face->bbox.yMax - face->bbox.yMin); - } else { - p.ascent = QFixed::fromFixed(face->size->metrics.ascender); - p.descent = QFixed::fromFixed(-face->size->metrics.descender); - p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender); - p.emSquare = face->size->metrics.y_ppem; -// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.); - p.boundingBox = QRectF(0, -p.ascent.toReal(), - face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() ); - } - p.italicAngle = 0; - p.capHeight = p.ascent; - p.lineWidth = face->underline_thickness; - - return p; -} - -bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const -{ - return ft_getSfntTable(face, tag, buffer, length); -} - -/* Some fonts (such as MingLiu rely on hinting to scale different - components to their correct sizes. While this is really broken (it - should be done in the component glyph itself, not the hinter) we - will have to live with it. - - This means we can not use FT_LOAD_NO_HINTING to get the glyph - outline. All we can do is to load the unscaled glyph and scale it - down manually when required. -*/ -static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale) -{ - x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM); - y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM); - FT_Vector *p = g->outline.points; - const FT_Vector *e = p + g->outline.n_points; - while (p < e) { - p->x = FT_MulFix(p->x, x_scale); - p->y = FT_MulFix(p->y, y_scale); - ++p; - } -} - -#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug -void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale) -{ - const qreal factor = 1/64.; - scaleOutline(face, g, x_scale, y_scale); - - QPointF cp = point.toPointF(); - - // convert the outline to a painter path - int i = 0; - for (int j = 0; j < g->outline.n_contours; ++j) { - int last_point = g->outline.contours[j]; - GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point; - QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); - if (!(g->outline.tags[i] & 1)) { // start point is not on curve: - if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve: - GLYPH2PATH_DEBUG() << " start and end point are not on curve"; - start = (QPointF(g->outline.points[last_point].x*factor, - -g->outline.points[last_point].y*factor) + start) / 2.0; - } else { - GLYPH2PATH_DEBUG() << " end point is on curve, start is not"; - start = QPointF(g->outline.points[last_point].x*factor, - -g->outline.points[last_point].y*factor); - } - --i; // to use original start point as control point below - } - start += cp; - GLYPH2PATH_DEBUG() << " start at" << start; - - path->moveTo(start); - QPointF c[4]; - c[0] = start; - int n = 1; - while (i < last_point) { - ++i; - c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); - GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i] - << ": on curve =" << (bool)(g->outline.tags[i] & 1); - ++n; - switch (g->outline.tags[i] & 3) { - case 2: - // cubic bezier element - if (n < 4) - continue; - c[3] = (c[3] + c[2])/2; - --i; - break; - case 0: - // quadratic bezier element - if (n < 3) - continue; - c[3] = (c[1] + c[2])/2; - c[2] = (2*c[1] + c[3])/3; - c[1] = (2*c[1] + c[0])/3; - --i; - break; - case 1: - case 3: - if (n == 2) { - GLYPH2PATH_DEBUG() << " lineTo" << c[1]; - path->lineTo(c[1]); - c[0] = c[1]; - n = 1; - continue; - } else if (n == 3) { - c[3] = c[2]; - c[2] = (2*c[1] + c[3])/3; - c[1] = (2*c[1] + c[0])/3; - } - break; - } - GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3]; - path->cubicTo(c[1], c[2], c[3]); - c[0] = c[3]; - n = 1; - } - - if (n == 1) { - GLYPH2PATH_DEBUG() << " closeSubpath"; - path->closeSubpath(); - } else { - c[3] = start; - if (n == 2) { - c[2] = (2*c[1] + c[3])/3; - c[1] = (2*c[1] + c[0])/3; - } - GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3]; - path->cubicTo(c[1], c[2], c[3]); - } - ++i; - } -} - -extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path); - -void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path) -{ - if (slot->format != FT_GLYPH_FORMAT_BITMAP - || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO) - return; - - QPointF cp = point.toPointF(); - qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY), - slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path); -} - -QFontEngineFT::Glyph::~Glyph() -{ - delete [] data; -} - -struct LcdFilterDummy -{ - static inline void filterPixel(uchar &, uchar &, uchar &) - {} -}; - -struct LcdFilterLegacy -{ - static inline void filterPixel(uchar &red, uchar &green, uchar &blue) - { - uint r = red, g = green, b = blue; - // intra-pixel filter used by the legacy filter (adopted from _ft_lcd_filter_legacy) - red = (r * uint(65538 * 9/13) + g * uint(65538 * 1/6) + b * uint(65538 * 1/13)) / 65536; - green = (r * uint(65538 * 3/13) + g * uint(65538 * 4/6) + b * uint(65538 * 3/13)) / 65536; - blue = (r * uint(65538 * 1/13) + g * uint(65538 * 1/6) + b * uint(65538 * 9/13)) / 65536; - } -}; - -template -static void convertRGBToARGB_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr) -{ - const int offs = bgr ? -1 : 1; - const int w = width * 3; - while (height--) { - uint *dd = dst; - for (int x = 0; x < w; x += 3) { - uchar red = src[x + 1 - offs]; - uchar green = src[x + 1]; - uchar blue = src[x + 1 + offs]; - LcdFilter::filterPixel(red, green, blue); - *dd++ = (0xFF << 24) | (red << 16) | (green << 8) | blue; - } - dst += width; - src += src_pitch; - } -} - -static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) -{ - if (!legacyFilter) - convertRGBToARGB_helper(src, dst, width, height, src_pitch, bgr); - else - convertRGBToARGB_helper(src, dst, width, height, src_pitch, bgr); -} - -template -static void convertRGBToARGB_V_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr) -{ - const int offs = bgr ? -src_pitch : src_pitch; - while (height--) { - for (int x = 0; x < width; x++) { - uchar red = src[x + src_pitch - offs]; - uchar green = src[x + src_pitch]; - uchar blue = src[x + src_pitch + offs]; - LcdFilter::filterPixel(red, green, blue); - *dst++ = (0XFF << 24) | (red << 16) | (green << 8) | blue; - } - src += 3*src_pitch; - } -} - -static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) -{ - if (!legacyFilter) - convertRGBToARGB_V_helper(src, dst, width, height, src_pitch, bgr); - else - convertRGBToARGB_V_helper(src, dst, width, height, src_pitch, bgr); -} - -static inline void convertGRAYToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch) -{ - while (height--) { - const uchar *p = src; - const uchar * const e = p + width; - while (p < e) { - uchar gray = *p++; - *dst++ = (0xFF << 24) | (gray << 16) | (gray << 8) | gray; - } - src += src_pitch; - } -} - -static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, int pitch) -{ - // convolute the bitmap with a triangle filter to get rid of color fringes - // If we take account for a gamma value of 2, we end up with - // weights of 1, 4, 9, 4, 1. We use an approximation of 1, 3, 8, 3, 1 here, - // as this nicely sums up to 16 :) - int h = height; - while (h--) { - dst[0] = dst[1] = 0; - // - for (int x = 2; x < width - 2; ++x) { - uint sum = src[x-2] + 3*src[x-1] + 8*src[x] + 3*src[x+1] + src[x+2]; - dst[x] = (uchar) (sum >> 4); - } - dst[width - 2] = dst[width - 1] = 0; - src += pitch; - dst += pitch; - } -} - -QFontEngineFT::QFontEngineFT(const QFontDef &fd) - : QFontEngine(Freetype) -{ - fontDef = fd; - matrix.xx = 0x10000; - matrix.yy = 0x10000; - matrix.xy = 0; - matrix.yx = 0; - cache_cost = 100 * 1024; - kerning_pairs_loaded = false; - transform = false; - embolden = false; - obliquen = false; - antialias = true; - freetype = 0; - default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; - default_hint_style = ftInitialDefaultHintStyle; - subpixelType = Subpixel_None; - lcdFilterType = 0; -#if defined(FT_LCD_FILTER_H) - lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT); -#endif - defaultFormat = Format_None; - embeddedbitmap = false; - const QByteArray env = qgetenv("QT_NO_FT_CACHE"); - cacheEnabled = env.isEmpty() || env.toInt() == 0; - m_subPixelPositionCount = 4; - forceAutoHint = false; - stemDarkeningDriver = false; -} - -QFontEngineFT::~QFontEngineFT() -{ - if (freetype) - freetype->release(face_id); -} - -bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, - const QByteArray &fontData) -{ - return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData)); -} - -static void dont_delete(void*) {} - -bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, - QFreetypeFace *freetypeFace) -{ - freetype = freetypeFace; - if (!freetype) { - xsize = 0; - ysize = 0; - return false; - } - defaultFormat = format; - this->antialias = antialias; - - if (!antialias) - glyphFormat = QFontEngine::Format_Mono; - else - glyphFormat = defaultFormat; - - face_id = faceId; - - symbol = freetype->symbol_map != 0; - 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 = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive)); - } - - freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor); - - FT_Face face = lockFace(); - - if (FT_IS_SCALABLE(face)) { - bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC); - if (fake_oblique) - obliquen = true; - FT_Set_Transform(face, &matrix, 0); - freetype->matrix = matrix; - // fake bold - if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face)) { - if (const TT_OS2 *os2 = reinterpret_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) { - if (os2->usWeightClass < 750) - 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)); - } else { - // ad hoc algorithm - int score = fontDef.weight * fontDef.pixelSize; - line_thickness = score / 700; - // 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()) { - glyphFormat = defaultFormat = GlyphFormat::Format_ARGB; - cacheEnabled = false; - } - } - if (line_thickness < 1) - line_thickness = 1; - - metrics = face->size->metrics; - - /* - 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 - the metrics are retrieved from the bitmap strikes. - */ - if (FT_IS_SCALABLE(face)) { - for (int i = 0; i < face->num_fixed_sizes; ++i) { - if (xsize == face->available_sizes[i].x_ppem && ysize == face->available_sizes[i].y_ppem) { - face->face_flags &= ~FT_FACE_FLAG_SCALABLE; - - FT_Select_Size(face, i); - if (face->size->metrics.ascender + face->size->metrics.descender > 0) { - FT_Pos leading = metrics.height - metrics.ascender + metrics.descender; - metrics.ascender = face->size->metrics.ascender; - metrics.descender = face->size->metrics.descender; - if (metrics.descender > 0 - && QString::fromUtf8(face->family_name) == QLatin1String("Courier New")) { - metrics.descender *= -1; - } - metrics.height = metrics.ascender - metrics.descender + leading; - } - FT_Set_Char_Size(face, xsize, ysize, 0, 0); - - face->face_flags |= FT_FACE_FLAG_SCALABLE; - break; - } - } - } -#if defined(FT_FONT_FORMATS_H) - const char *fmt = FT_Get_Font_Format(face); - if (fmt && qstrncmp(fmt, "CFF", 4) == 0) { - FT_Bool no_stem_darkening = true; - FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening); - if (err == FT_Err_Ok) - stemDarkeningDriver = !no_stem_darkening; - else - stemDarkeningDriver = false; - } -#endif - - fontDef.styleName = QString::fromUtf8(face->style_name); - - if (!freetype->hbFace) { - faceData.user_data = face; - faceData.get_font_table = ft_getSfntTable; - (void)harfbuzzFace(); // populates face_ - freetype->hbFace = std::move(face_); - } else { - Q_ASSERT(!face_); - } - // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it - face_ = Holder(freetype->hbFace.get(), dont_delete); - - unlockFace(); - - fsType = freetype->fsType(); - return true; -} - -void QFontEngineFT::setQtDefaultHintStyle(QFont::HintingPreference hintingPreference) -{ - switch (hintingPreference) { - case QFont::PreferNoHinting: - setDefaultHintStyle(HintNone); - break; - case QFont::PreferFullHinting: - setDefaultHintStyle(HintFull); - break; - case QFont::PreferVerticalHinting: - setDefaultHintStyle(HintLight); - break; - case QFont::PreferDefaultHinting: - setDefaultHintStyle(ftInitialDefaultHintStyle); - break; - } -} - -void QFontEngineFT::setDefaultHintStyle(HintStyle style) -{ - default_hint_style = style; -} - -bool QFontEngineFT::expectsGammaCorrectedBlending() const -{ - return stemDarkeningDriver; -} - -int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags, - bool &hsubpixel, int &vfactor) const -{ - int load_flags = FT_LOAD_DEFAULT | default_load_flags; - int load_target = default_hint_style == HintLight - ? FT_LOAD_TARGET_LIGHT - : FT_LOAD_TARGET_NORMAL; - - if (format == Format_Mono) { - load_target = FT_LOAD_TARGET_MONO; - } else if (format == Format_A32) { - if (subpixelType == Subpixel_RGB || subpixelType == Subpixel_BGR) { - if (default_hint_style == HintFull) - load_target = FT_LOAD_TARGET_LCD; - hsubpixel = true; - } else if (subpixelType == Subpixel_VRGB || subpixelType == Subpixel_VBGR) { - if (default_hint_style == HintFull) - load_target = FT_LOAD_TARGET_LCD_V; - vfactor = 3; - } - } else if (format == Format_ARGB) { -#ifdef FT_LOAD_COLOR - load_flags |= FT_LOAD_COLOR; -#endif - } - - if (set && set->outline_drawing) - load_flags |= FT_LOAD_NO_BITMAP; - - if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing)) - load_flags |= FT_LOAD_NO_HINTING; - else - load_flags |= load_target; - - if (forceAutoHint) - load_flags |= FT_LOAD_FORCE_AUTOHINT; - - return load_flags; -} - -static inline bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info) -{ - // false if exceeds QFontEngineFT::Glyph metrics - return (short)(info.linearAdvance) != info.linearAdvance - || (uchar)(info.width) != info.width - || (uchar)(info.height) != info.height; -} - -static inline void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix) -{ - int l, r, t, b; - FT_Vector vector; - vector.x = *left; - vector.y = *top; - FT_Vector_Transform(&vector, matrix); - l = r = vector.x; - t = b = vector.y; - vector.x = *right; - vector.y = *top; - FT_Vector_Transform(&vector, matrix); - if (l > vector.x) l = vector.x; - if (r < vector.x) r = vector.x; - if (t < vector.y) t = vector.y; - if (b > vector.y) b = vector.y; - vector.x = *right; - vector.y = *bottom; - FT_Vector_Transform(&vector, matrix); - if (l > vector.x) l = vector.x; - if (r < vector.x) r = vector.x; - if (t < vector.y) t = vector.y; - if (b > vector.y) b = vector.y; - vector.x = *left; - vector.y = *bottom; - FT_Vector_Transform(&vector, matrix); - if (l > vector.x) l = vector.x; - if (r < vector.x) r = vector.x; - if (t < vector.y) t = vector.y; - if (b > vector.y) b = vector.y; - *left = l; - *right = r; - *top = t; - *bottom = b; -} - -QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, - QFixed subPixelPosition, - GlyphFormat format, - bool fetchMetricsOnly, - bool disableOutlineDrawing) const -{ -// Q_ASSERT(freetype->lock == 1); - - if (format == Format_None) - format = defaultFormat != Format_None ? defaultFormat : Format_Mono; - Q_ASSERT(format != Format_None); - - Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : 0; - if (g && g->format == format && (fetchMetricsOnly || g->data)) - return g; - - if (!g && set && set->isGlyphMissing(glyph)) - return &emptyGlyph; - - - FT_Face face = freetype->face; - - FT_Matrix matrix = freetype->matrix; - - FT_Vector v; - v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.value()); - v.y = 0; - FT_Set_Transform(face, &matrix, &v); - - bool hsubpixel = false; - int vfactor = 1; - int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor); - - bool transform = matrix.xx != 0x10000 - || matrix.yy != 0x10000 - || matrix.xy != 0 - || matrix.yx != 0; - - if (transform || (format != Format_Mono && !isScalableBitmap())) - load_flags |= FT_LOAD_NO_BITMAP; - - FT_Error err = FT_Load_Glyph(face, glyph, load_flags); - if (err && (load_flags & FT_LOAD_NO_BITMAP)) { - load_flags &= ~FT_LOAD_NO_BITMAP; - err = FT_Load_Glyph(face, glyph, load_flags); - } - if (err == FT_Err_Too_Few_Arguments) { - // this is an error in the bytecode interpreter, just try to run without it - load_flags |= FT_LOAD_FORCE_AUTOHINT; - err = FT_Load_Glyph(face, glyph, load_flags); - } else if (err == FT_Err_Execution_Too_Long) { - // This is an error in the bytecode, probably a web font made by someone who - // didn't test bytecode hinting at all so disable for it for all glyphs. - qWarning("load glyph failed due to broken hinting bytecode in font, switching to auto hinting"); - default_load_flags |= FT_LOAD_FORCE_AUTOHINT; - load_flags |= FT_LOAD_FORCE_AUTOHINT; - err = FT_Load_Glyph(face, glyph, load_flags); - } - if (err != FT_Err_Ok) { - qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph); - if (set) - set->setGlyphMissing(glyph); - return &emptyGlyph; - } - - FT_GlyphSlot slot = face->glyph; - - if (embolden) - FT_GlyphSlot_Embolden(slot); - if (obliquen) { - FT_GlyphSlot_Oblique(slot); - - // While Embolden alters the metrics of the slot, oblique does not, so we need - // to fix this ourselves. - transform = true; - FT_Matrix m; - m.xx = 0x10000; - m.yx = 0x0; - m.xy = 0x6000; - m.yy = 0x10000; - - FT_Matrix_Multiply(&m, &matrix); - } - - GlyphInfo info; - info.linearAdvance = slot->linearHoriAdvance >> 10; - info.xOff = TRUNC(ROUND(slot->advance.x)); - info.yOff = 0; - - if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) { - int left = slot->metrics.horiBearingX; - int right = slot->metrics.horiBearingX + slot->metrics.width; - int top = slot->metrics.horiBearingY; - int bottom = slot->metrics.horiBearingY - slot->metrics.height; - - if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP) - transformBoundingBox(&left, &top, &right, &bottom, &matrix); - - left = FLOOR(left); - right = CEIL(right); - bottom = FLOOR(bottom); - top = CEIL(top); - - info.x = TRUNC(left); - info.y = TRUNC(top); - info.width = TRUNC(right - left); - info.height = TRUNC(top - bottom); - - // If any of the metrics are too large to fit, don't cache them - if (areMetricsTooLarge(info)) - return 0; - - g = new Glyph; - g->data = 0; - g->linearAdvance = info.linearAdvance; - g->width = info.width; - g->height = info.height; - g->x = info.x; - g->y = info.y; - g->advance = info.xOff; - g->format = format; - - if (set) - set->setGlyph(glyph, subPixelPosition, g); - - return g; - } - - int glyph_buffer_size = 0; - QScopedArrayPointer glyph_buffer; -#if defined(QT_USE_FREETYPE_LCDFILTER) - bool useFreetypeRenderGlyph = false; - if (slot->format == FT_GLYPH_FORMAT_OUTLINE && (hsubpixel || vfactor != 1)) { - err = FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType); - if (err == FT_Err_Ok) - useFreetypeRenderGlyph = true; - } - - if (useFreetypeRenderGlyph) { - err = FT_Render_Glyph(slot, hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V); - - if (err != FT_Err_Ok) - qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph); - - FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE); - - info.height = slot->bitmap.rows / vfactor; - info.width = hsubpixel ? slot->bitmap.width / 3 : slot->bitmap.width; - info.x = slot->bitmap_left; - info.y = slot->bitmap_top; - - glyph_buffer_size = info.width * info.height * 4; - glyph_buffer.reset(new uchar[glyph_buffer_size]); - - if (hsubpixel) - convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB, false); - else if (vfactor != 1) - convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB, false); - } else -#endif - { - int left = slot->metrics.horiBearingX; - int right = slot->metrics.horiBearingX + slot->metrics.width; - int top = slot->metrics.horiBearingY; - int bottom = slot->metrics.horiBearingY - slot->metrics.height; - if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP) - transformBoundingBox(&left, &top, &right, &bottom, &matrix); - left = FLOOR(left); - right = CEIL(right); - bottom = FLOOR(bottom); - top = CEIL(top); - - int hpixels = TRUNC(right - left); - // subpixel position requires one more pixel - if (subPixelPosition > 0 && format != Format_Mono) - hpixels++; - - if (hsubpixel) - hpixels = hpixels*3 + 8; - info.width = hpixels; - info.height = TRUNC(top - bottom); - info.x = TRUNC(left); - info.y = TRUNC(top); - if (hsubpixel) { - info.width /= 3; - info.x -= 1; - } - - // If any of the metrics are too large to fit, don't cache them - if (areMetricsTooLarge(info)) - return 0; - - int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 : - (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4)); - if (glyph_buffer_size < pitch * info.height) { - glyph_buffer_size = pitch * info.height; - glyph_buffer.reset(new uchar[glyph_buffer_size]); - memset(glyph_buffer.data(), 0, glyph_buffer_size); - } - - if (slot->format == FT_GLYPH_FORMAT_OUTLINE) { - FT_Bitmap bitmap; - bitmap.rows = info.height*vfactor; - bitmap.width = hpixels; - bitmap.pitch = format == Format_Mono ? (((info.width + 31) & ~31) >> 3) : ((bitmap.width + 3) & ~3); - int bitmap_buffer_size = bitmap.rows * bitmap.pitch; - if (!hsubpixel && vfactor == 1 && format != Format_A32) { - Q_ASSERT(glyph_buffer_size <= bitmap_buffer_size); - bitmap.buffer = glyph_buffer.data(); - } else { - bitmap.buffer = new uchar[bitmap_buffer_size]; - memset(bitmap.buffer, 0, bitmap_buffer_size); - } - bitmap.pixel_mode = format == Format_Mono ? FT_PIXEL_MODE_MONO : FT_PIXEL_MODE_GRAY; - FT_Matrix matrix; - matrix.xx = (hsubpixel ? 3 : 1) << 16; - matrix.yy = vfactor << 16; - matrix.yx = matrix.xy = 0; - - FT_Outline_Transform(&slot->outline, &matrix); - FT_Outline_Translate (&slot->outline, (hsubpixel ? -3*left +(4<<6) : -left), -bottom*vfactor); - FT_Outline_Get_Bitmap(slot->library, &slot->outline, &bitmap); - if (hsubpixel) { - Q_ASSERT (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); - Q_ASSERT(antialias); - uchar *convoluted = new uchar[bitmap_buffer_size]; - bool useLegacyLcdFilter = false; -#if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H) - useLegacyLcdFilter = (lcdFilterType == FT_LCD_FILTER_LEGACY); -#endif - uchar *buffer = bitmap.buffer; - if (!useLegacyLcdFilter) { - convoluteBitmap(bitmap.buffer, convoluted, bitmap.width, info.height, bitmap.pitch); - buffer = convoluted; - } - convertRGBToARGB(buffer + 1, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_RGB, useLegacyLcdFilter); - delete [] convoluted; - } else if (vfactor != 1) { - convertRGBToARGB_V(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_VRGB, true); - } else if (format == Format_A32 && bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { - convertGRAYToARGB(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch); - } - - if (bitmap.buffer != glyph_buffer.data()) - delete [] bitmap.buffer; - } else if (slot->format == FT_GLYPH_FORMAT_BITMAP) { -#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500) - Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO || slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA); -#else - Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO); -#endif - uchar *src = slot->bitmap.buffer; - uchar *dst = glyph_buffer.data(); - int h = slot->bitmap.rows; - if (format == Format_Mono) { - int bytes = ((info.width + 7) & ~7) >> 3; - while (h--) { - memcpy (dst, src, bytes); - dst += pitch; - src += slot->bitmap.pitch; - } - } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { - if (hsubpixel) { - while (h--) { - uint *dd = (uint *)dst; - *dd++ = 0; - for (int x = 0; x < static_cast(slot->bitmap.width); x++) { - uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); - *dd++ = a; - } - *dd++ = 0; - dst += pitch; - src += slot->bitmap.pitch; - } - } else if (vfactor != 1) { - while (h--) { - uint *dd = (uint *)dst; - for (int x = 0; x < static_cast(slot->bitmap.width); x++) { - uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); - *dd++ = a; - } - dst += pitch; - src += slot->bitmap.pitch; - } - } else { - while (h--) { - for (int x = 0; x < static_cast(slot->bitmap.width); x++) { - unsigned char a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00); - dst[x] = a; - } - dst += pitch; - src += slot->bitmap.pitch; - } - } - } -#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500) - else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) - { - while (h--) { -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - const quint32 *srcPixel = (const quint32 *)src; - quint32 *dstPixel = (quint32 *)dst; - for (int x = 0; x < static_cast(slot->bitmap.width); x++, srcPixel++, dstPixel++) { - const quint32 pixel = *srcPixel; - *dstPixel = qbswap(pixel); - } -#else - memcpy(dst, src, slot->bitmap.width * 4); -#endif - dst += slot->bitmap.pitch; - src += slot->bitmap.pitch; - } - info.width = info.linearAdvance = info.xOff = slot->bitmap.width; - info.height = slot->bitmap.rows; - info.x = slot->bitmap_left; - info.y = slot->bitmap_top; - } -#endif - } else { - qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format); - return 0; - } - } - - - if (!g) { - g = new Glyph; - g->data = 0; - } - - g->linearAdvance = info.linearAdvance; - g->width = info.width; - g->height = info.height; - g->x = info.x; - g->y = info.y; - g->advance = info.xOff; - g->format = format; - delete [] g->data; - g->data = glyph_buffer.take(); - - if (set) - set->setGlyph(glyph, subPixelPosition, g); - - return g; -} - -QFontEngine::FaceId QFontEngineFT::faceId() const -{ - return face_id; -} - -QFontEngine::Properties QFontEngineFT::properties() const -{ - Properties p = freetype->properties(); - if (p.postscriptName.isEmpty()) { - p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8()); - } - - return freetype->properties(); -} - -QFixed QFontEngineFT::emSquareSize() const -{ - if (FT_IS_SCALABLE(freetype->face)) - return freetype->face->units_per_EM; - else - return freetype->face->size->metrics.y_ppem; -} - -bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const -{ - return ft_getSfntTable(freetype->face, tag, buffer, length); -} - -int QFontEngineFT::synthesized() const -{ - int s = 0; - if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)) - s = SynthesizedItalic; - if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD)) - s |= SynthesizedBold; - if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face)) - s |= SynthesizedStretch; - return s; -} - -QFixed QFontEngineFT::ascent() const -{ - QFixed v = QFixed::fromFixed(metrics.ascender); - if (scalableBitmapScaleFactor != 1) - v *= scalableBitmapScaleFactor; - return v; -} - -QFixed QFontEngineFT::capHeight() const -{ - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); - if (os2 && os2->version >= 2) { - lockFace(); - QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale)); - unlockFace(); - return answer; - } - return calculatedCapHeight(); -} - -QFixed QFontEngineFT::descent() const -{ - QFixed v = QFixed::fromFixed(-metrics.descender); - if (scalableBitmapScaleFactor != 1) - v *= scalableBitmapScaleFactor; - return v; -} - -QFixed QFontEngineFT::leading() const -{ - QFixed v = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender); - if (scalableBitmapScaleFactor != 1) - v *= scalableBitmapScaleFactor; - return v; -} - -QFixed QFontEngineFT::xHeight() const -{ - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); - if (os2 && os2->sxHeight) { - lockFace(); - QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize(); - unlockFace(); - return answer; - } - - return QFontEngine::xHeight(); -} - -QFixed QFontEngineFT::averageCharWidth() const -{ - TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); - if (os2 && os2->xAvgCharWidth) { - lockFace(); - QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize(); - unlockFace(); - return answer; - } - - return QFontEngine::averageCharWidth(); -} - -qreal QFontEngineFT::maxCharWidth() const -{ - QFixed max_advance = QFixed::fromFixed(metrics.max_advance); - if (scalableBitmapScaleFactor != 1) - max_advance *= scalableBitmapScaleFactor; - return max_advance.toReal(); -} - -QFixed QFontEngineFT::lineThickness() const -{ - return line_thickness; -} - -QFixed QFontEngineFT::underlinePosition() const -{ - return underline_position; -} - -void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const -{ - if (!kerning_pairs_loaded) { - kerning_pairs_loaded = true; - lockFace(); - if (freetype->face->size->metrics.x_ppem != 0) { - QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem); - unlockFace(); - const_cast(this)->loadKerningPairs(scalingFactor); - } else { - unlockFace(); - } - } - - if (shouldUseDesignMetrics(flags) && !(fontDef.styleStrategy & QFont::ForceIntegerMetrics)) - flags |= DesignMetrics; - else - flags &= ~DesignMetrics; - - QFontEngine::doKerning(g, flags); -} - -static inline FT_Matrix QTransformToFTMatrix(const QTransform &matrix) -{ - FT_Matrix m; - - m.xx = FT_Fixed(matrix.m11() * 65536); - m.xy = FT_Fixed(-matrix.m21() * 65536); - m.yx = FT_Fixed(-matrix.m12() * 65536); - m.yy = FT_Fixed(matrix.m22() * 65536); - - return m; -} - -QFontEngineFT::QGlyphSet *QFontEngineFT::loadGlyphSet(const QTransform &matrix) -{ - if (matrix.type() > QTransform::TxShear || !cacheEnabled) - return 0; - - // FT_Set_Transform only supports scalable fonts - if (!FT_IS_SCALABLE(freetype->face)) - return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet : Q_NULLPTR; - - FT_Matrix m = QTransformToFTMatrix(matrix); - - QGlyphSet *gs = 0; - - for (int i = 0; i < transformedGlyphSets.count(); ++i) { - const QGlyphSet &g = transformedGlyphSets.at(i); - if (g.transformationMatrix.xx == m.xx - && g.transformationMatrix.xy == m.xy - && g.transformationMatrix.yx == m.yx - && g.transformationMatrix.yy == m.yy) { - - // found a match, move it to the front - transformedGlyphSets.move(i, 0); - gs = &transformedGlyphSets[0]; - break; - } - } - - if (!gs) { - // don't cache more than 10 transformations - if (transformedGlyphSets.count() >= 10) { - transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0); - } else { - transformedGlyphSets.prepend(QGlyphSet()); - } - gs = &transformedGlyphSets[0]; - gs->clear(); - gs->transformationMatrix = m; - gs->outline_drawing = fontDef.pixelSize * fontDef.pixelSize * qAbs(matrix.det()) >= QT_MAX_CACHED_GLYPH_SIZE * QT_MAX_CACHED_GLYPH_SIZE; - } - Q_ASSERT(gs != 0); - - return gs; -} - -void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) -{ - FT_Face face = lockFace(Unscaled); - FT_Set_Transform(face, 0, 0); - FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); - - int left = face->glyph->metrics.horiBearingX; - int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width; - int top = face->glyph->metrics.horiBearingY; - int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height; - - QFixedPoint p; - p.x = 0; - p.y = 0; - - metrics->width = QFixed::fromFixed(right-left); - metrics->height = QFixed::fromFixed(top-bottom); - metrics->x = QFixed::fromFixed(left); - metrics->y = QFixed::fromFixed(-top); - metrics->xoff = QFixed::fromFixed(face->glyph->advance.x); - - if (!FT_IS_SCALABLE(freetype->face)) - QFreetypeFace::addBitmapToPath(face->glyph, p, path); - else - QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6); - - FT_Set_Transform(face, &freetype->matrix, 0); - unlockFace(); -} - -bool QFontEngineFT::supportsTransformation(const QTransform &transform) const -{ - return transform.type() <= QTransform::TxRotate; -} - -void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) -{ - if (!glyphs.numGlyphs) - return; - - if (FT_IS_SCALABLE(freetype->face)) { - QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); - } else { - QVarLengthArray positions; - QVarLengthArray positioned_glyphs; - QTransform matrix; - matrix.translate(x, y); - getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions); - - FT_Face face = lockFace(Unscaled); - for (int gl = 0; gl < glyphs.numGlyphs; gl++) { - FT_UInt glyph = positioned_glyphs[gl]; - FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO); - QFreetypeFace::addBitmapToPath(face->glyph, positions[gl], path); - } - unlockFace(); - } -} - -void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, - QPainterPath *path, QTextItem::RenderFlags) -{ - FT_Face face = lockFace(Unscaled); - - for (int gl = 0; gl < numGlyphs; gl++) { - FT_UInt glyph = glyphs[gl]; - - FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); - - FT_GlyphSlot g = face->glyph; - if (g->format != FT_GLYPH_FORMAT_OUTLINE) - continue; - if (embolden) - FT_GlyphSlot_Embolden(g); - if (obliquen) - FT_GlyphSlot_Oblique(g); - QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize); - } - unlockFace(); -} - -glyph_t QFontEngineFT::glyphIndex(uint ucs4) const -{ - glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0; - if (glyph == 0) { - FT_Face face = freetype->face; - glyph = FT_Get_Char_Index(face, ucs4); - if (glyph == 0) { - // Certain fonts don't have no-break space and tab, - // while we usually want to render them as space - if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) { - glyph = FT_Get_Char_Index(face, QChar::Space); - } else if (freetype->symbol_map) { - // Symbol fonts can have more than one CMAPs, FreeType should take the - // correct one for us by default, so we always try FT_Get_Char_Index - // first. If it didn't work (returns 0), we will explicitly set the - // CMAP to symbol font one and try again. symbol_map is not always the - // correct one because in certain fonts like Wingdings symbol_map only - // contains PUA codepoints instead of the common ones. - FT_Set_Charmap(face, freetype->symbol_map); - glyph = FT_Get_Char_Index(face, ucs4); - FT_Set_Charmap(face, freetype->unicode_map); - } - } - if (ucs4 < QFreetypeFace::cmapCacheSize) - freetype->cmapCache[ucs4] = glyph; - } - - return glyph; -} - -bool 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; - } - - int glyph_pos = 0; - if (freetype->symbol_map) { - FT_Face face = freetype->face; - QStringIterator it(str, str + len); - while (it.hasNext()) { - uint uc = it.next(); - glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; - if ( !glyphs->glyphs[glyph_pos] ) { - // Symbol fonts can have more than one CMAPs, FreeType should take the - // correct one for us by default, so we always try FT_Get_Char_Index - // first. If it didn't work (returns 0), we will explicitly set the - // CMAP to symbol font one and try again. symbol_map is not always the - // correct one because in certain fonts like Wingdings symbol_map only - // contains PUA codepoints instead of the common ones. - glyph_t glyph = FT_Get_Char_Index(face, uc); - // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9), - // while we usually want to render them as space - if (!glyph && (uc == 0xa0 || uc == 0x9)) { - uc = 0x20; - glyph = FT_Get_Char_Index(face, uc); - } - if (!glyph) { - FT_Set_Charmap(face, freetype->symbol_map); - glyph = FT_Get_Char_Index(face, uc); - FT_Set_Charmap(face, freetype->unicode_map); - } - glyphs->glyphs[glyph_pos] = glyph; - if (uc < QFreetypeFace::cmapCacheSize) - freetype->cmapCache[uc] = glyph; - } - ++glyph_pos; - } - } else { - FT_Face face = freetype->face; - QStringIterator it(str, str + len); - while (it.hasNext()) { - uint uc = it.next(); - glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; - if (!glyphs->glyphs[glyph_pos]) { - { - redo: - glyph_t glyph = FT_Get_Char_Index(face, uc); - if (!glyph && (uc == 0xa0 || uc == 0x9)) { - uc = 0x20; - goto redo; - } - glyphs->glyphs[glyph_pos] = glyph; - if (uc < QFreetypeFace::cmapCacheSize) - freetype->cmapCache[uc] = glyph; - } - } - ++glyph_pos; - } - } - - *nglyphs = glyph_pos; - glyphs->numGlyphs = glyph_pos; - - if (!(flags & GlyphIndicesOnly)) - recalcAdvances(glyphs, flags); - - return true; -} - -bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const -{ - if (!FT_IS_SCALABLE(freetype->face)) - return false; - - return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics); -} - -QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const -{ - return m * scalableBitmapScaleFactor; -} - -glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m) const -{ - glyph_metrics_t metrics; - metrics.x = scaledBitmapMetrics(m.x); - metrics.y = scaledBitmapMetrics(m.y); - metrics.width = scaledBitmapMetrics(m.width); - metrics.height = scaledBitmapMetrics(m.height); - metrics.xoff = scaledBitmapMetrics(m.xoff); - metrics.yoff = scaledBitmapMetrics(m.yoff); - return metrics; -} - -void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const -{ - FT_Face face = 0; - bool design = shouldUseDesignMetrics(flags); - for (int i = 0; i < glyphs->numGlyphs; i++) { - Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : 0; - // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph - GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono; - if (g && g->format == acceptableFormat) { - glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); - } else { - if (!face) - face = lockFace(); - g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs->glyphs[i], 0, Format_None, true); - if (g) - glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); - else - glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10) - : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round(); - if (!cacheEnabled && g != &emptyGlyph) - delete g; - } - - if (scalableBitmapScaleFactor != 1) - glyphs->advances[i] *= scalableBitmapScaleFactor; - } - if (face) - unlockFace(); - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - for (int i = 0; i < glyphs->numGlyphs; ++i) - glyphs->advances[i] = glyphs->advances[i].round(); - } -} - -glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) -{ - FT_Face face = 0; - - glyph_metrics_t overall; - // initialize with line height, we get the same behaviour on all platforms - if (!isScalableBitmap()) { - overall.y = -ascent(); - overall.height = ascent() + descent(); - } else { - overall.y = QFixed::fromFixed(-metrics.ascender); - overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender); - } - - QFixed ymax = 0; - QFixed xmax = 0; - for (int i = 0; i < glyphs.numGlyphs; i++) { - Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : 0; - if (!g) { - if (!face) - face = lockFace(); - g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs.glyphs[i], 0, Format_None, true); - } - if (g) { - QFixed x = overall.xoff + glyphs.offsets[i].x + g->x; - 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; - if (!cacheEnabled && g != &emptyGlyph) - delete g; - } else { - int left = FLOOR(face->glyph->metrics.horiBearingX); - int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); - int top = CEIL(face->glyph->metrics.horiBearingY); - int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); - - QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left)); - QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top); - overall.x = qMin(overall.x, x); - 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.height = qMax(overall.height, ymax - overall.y); - overall.width = xmax - overall.x; - - if (face) - unlockFace(); - - if (isScalableBitmap()) - overall = scaledBitmapMetrics(overall); - return overall; -} - -glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) -{ - FT_Face face = 0; - glyph_metrics_t overall; - Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : 0; - if (!g) { - face = lockFace(); - g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, 0, Format_None, true); - } - if (g) { - overall.x = g->x; - overall.y = -g->y; - overall.width = g->width; - overall.height = g->height; - overall.xoff = g->advance; - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - overall.xoff = overall.xoff.round(); - if (!cacheEnabled && g != &emptyGlyph) - delete g; - } else { - int left = FLOOR(face->glyph->metrics.horiBearingX); - int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); - int top = CEIL(face->glyph->metrics.horiBearingY); - int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); - - overall.width = TRUNC(right-left); - overall.height = TRUNC(top-bottom); - overall.x = TRUNC(left); - overall.y = -TRUNC(top); - overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); - } - if (face) - unlockFace(); - - if (isScalableBitmap()) - overall = scaledBitmapMetrics(overall); - return overall; -} - -glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix) -{ - return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None); -} - -glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format) -{ - Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true); - - glyph_metrics_t overall; - if (g) { - overall.x = g->x; - overall.y = -g->y; - overall.width = g->width; - overall.height = g->height; - overall.xoff = g->advance; - if (!cacheEnabled && g != &emptyGlyph) - delete g; - } else { - FT_Face face = lockFace(); - int left = FLOOR(face->glyph->metrics.horiBearingX); - int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); - int top = CEIL(face->glyph->metrics.horiBearingY); - int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); - - overall.width = TRUNC(right-left); - overall.height = TRUNC(top-bottom); - overall.x = TRUNC(left); - overall.y = -TRUNC(top); - overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); - unlockFace(); - } - - if (isScalableBitmap()) - overall = scaledBitmapMetrics(overall); - return overall; -} - -static inline QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEngine::GlyphFormat glyphFormat) -{ - if (glyph == Q_NULLPTR || glyph->height == 0 || glyph->width == 0) - return QImage(); - - QImage::Format format = QImage::Format_Invalid; - int bytesPerLine = -1; - switch (glyphFormat) { - case QFontEngine::Format_Mono: - format = QImage::Format_Mono; - bytesPerLine = ((glyph->width + 31) & ~31) >> 3; - break; - case QFontEngine::Format_A8: - format = QImage::Format_Alpha8; - bytesPerLine = (glyph->width + 3) & ~3; - break; - case QFontEngine::Format_A32: - format = QImage::Format_RGB32; - bytesPerLine = glyph->width * 4; - break; - default: - Q_UNREACHABLE(); - }; - - QImage img(static_cast(glyph->data), glyph->width, glyph->height, bytesPerLine, format); - if (format == QImage::Format_Mono) - img.setColor(1, QColor(Qt::white).rgba()); // Expands color table to 2 items; item 0 set to transparent. - return img; -} - -QImage *QFontEngineFT::lockedAlphaMapForGlyph(glyph_t glyphIndex, QFixed subPixelPosition, - QFontEngine::GlyphFormat neededFormat, - const QTransform &t, QPoint *offset) -{ - Q_ASSERT(currentlyLockedAlphaMap.isNull()); - - if (isBitmapFont()) - neededFormat = Format_Mono; - else if (neededFormat == Format_None && defaultFormat != Format_None) - neededFormat = defaultFormat; - else if (neededFormat == Format_None) - neededFormat = Format_A8; - - Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t); - - if (offset != 0 && glyph != 0) - *offset = QPoint(glyph->x, -glyph->y); - - currentlyLockedAlphaMap = alphaMapFromGlyphData(glyph, neededFormat); - - const bool glyphHasGeometry = glyph != Q_NULLPTR && glyph->height != 0 && glyph->width != 0; - if (!cacheEnabled && glyph != &emptyGlyph) { - currentlyLockedAlphaMap = currentlyLockedAlphaMap.copy(); - delete glyph; - } - - if (!glyphHasGeometry) - return Q_NULLPTR; - - if (currentlyLockedAlphaMap.isNull()) - return QFontEngine::lockedAlphaMapForGlyph(glyphIndex, subPixelPosition, neededFormat, t, offset); - - QImageData *data = currentlyLockedAlphaMap.data_ptr(); - data->is_locked = true; - - return ¤tlyLockedAlphaMap; -} - -void QFontEngineFT::unlockAlphaMapForGlyph() -{ - QFontEngine::unlockAlphaMapForGlyph(); -} - -static inline bool is2dRotation(const QTransform &t) -{ - return qFuzzyCompare(t.m11(), t.m22()) && qFuzzyCompare(t.m12(), -t.m21()) - && qFuzzyCompare(t.m11()*t.m22() - t.m12()*t.m21(), qreal(1.0)); -} - -QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, - QFixed subPixelPosition, - GlyphFormat format, - const QTransform &t, - bool fetchBoundingBox, - bool disableOutlineDrawing) -{ - QGlyphSet *glyphSet = loadGlyphSet(t); - if (glyphSet != 0 && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox) - return 0; - - Glyph *glyph = glyphSet != 0 ? glyphSet->getGlyph(g, subPixelPosition) : 0; - if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) { - QScopedValueRollback saved_default_hint_style(default_hint_style); - if (t.type() >= QTransform::TxScale && !is2dRotation(t)) - default_hint_style = HintNone; // disable hinting if the glyphs are transformed - - lockFace(); - FT_Matrix m = this->matrix; - FT_Matrix ftMatrix = glyphSet != 0 ? glyphSet->transformationMatrix : QTransformToFTMatrix(t); - FT_Matrix_Multiply(&ftMatrix, &m); - freetype->matrix = m; - glyph = loadGlyph(glyphSet, g, subPixelPosition, format, false, disableOutlineDrawing); - unlockFace(); - } - - return glyph; -} - -QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition) -{ - return alphaMapForGlyph(g, subPixelPosition, QTransform()); -} - -QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) -{ - const GlyphFormat neededFormat = antialias ? Format_A8 : Format_Mono; - - Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); - - QImage img = alphaMapFromGlyphData(glyph, neededFormat); - img = img.copy(); - - if (!cacheEnabled && glyph != &emptyGlyph) - delete glyph; - - if (!img.isNull()) - return img; - - return QFontEngine::alphaMapForGlyph(g, subPixelPosition, t); -} - -QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) -{ - if (t.type() > QTransform::TxRotate) - return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); - - const GlyphFormat neededFormat = Format_A32; - - Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); - - QImage img = alphaMapFromGlyphData(glyph, neededFormat); - img = img.copy(); - - if (!cacheEnabled && glyph != &emptyGlyph) - delete glyph; - - if (!img.isNull()) - return img; - - return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); -} - -QImage QFontEngineFT::bitmapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) -{ - Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t); - if (glyph == Q_NULLPTR) - return QImage(); - - QImage img; - if (defaultFormat == GlyphFormat::Format_ARGB) - img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy(); - else if (defaultFormat == GlyphFormat::Format_Mono) - img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy(); - - if (!img.isNull() && (!t.isIdentity() || scalableBitmapScaleFactor != 1)) { - QTransform trans(t); - const qreal scaleFactor = scalableBitmapScaleFactor.toReal(); - trans.scale(scaleFactor, scaleFactor); - img = img.transformed(trans, Qt::SmoothTransformation); - } - - if (!cacheEnabled && glyph != &emptyGlyph) - delete glyph; - - return img; -} - -void QFontEngineFT::removeGlyphFromCache(glyph_t glyph) -{ - defaultGlyphSet.removeGlyphFromCache(glyph, 0); -} - -int QFontEngineFT::glyphCount() const -{ - int count = 0; - FT_Face face = lockFace(); - if (face) { - count = face->num_glyphs; - unlockFace(); - } - return count; -} - -FT_Face QFontEngineFT::lockFace(Scaling scale) const -{ - freetype->lock(); - FT_Face face = freetype->face; - if (scale == Unscaled) { - if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) { - freetype->xsize = face->units_per_EM << 6; - freetype->ysize = face->units_per_EM << 6; - } - } else if (freetype->xsize != xsize || freetype->ysize != ysize) { - FT_Set_Char_Size(face, xsize, ysize, 0, 0); - freetype->xsize = xsize; - freetype->ysize = ysize; - } - if (freetype->matrix.xx != matrix.xx || - freetype->matrix.yy != matrix.yy || - freetype->matrix.xy != matrix.xy || - freetype->matrix.yx != matrix.yx) { - freetype->matrix = matrix; - FT_Set_Transform(face, &freetype->matrix, 0); - } - - return face; -} - -void QFontEngineFT::unlockFace() const -{ - freetype->unlock(); -} - -FT_Face QFontEngineFT::non_locked_face() const -{ - return freetype->face; -} - - -QFontEngineFT::QGlyphSet::QGlyphSet() - : outline_drawing(false) -{ - transformationMatrix.xx = 0x10000; - transformationMatrix.yy = 0x10000; - transformationMatrix.xy = 0; - transformationMatrix.yx = 0; - memset(fast_glyph_data, 0, sizeof(fast_glyph_data)); - fast_glyph_count = 0; -} - -QFontEngineFT::QGlyphSet::~QGlyphSet() -{ - clear(); -} - -void QFontEngineFT::QGlyphSet::clear() -{ - if (fast_glyph_count > 0) { - for (int i = 0; i < 256; ++i) { - if (fast_glyph_data[i]) { - delete fast_glyph_data[i]; - fast_glyph_data[i] = 0; - } - } - fast_glyph_count = 0; - } - qDeleteAll(glyph_data); - glyph_data.clear(); -} - -void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition) -{ - if (useFastGlyphData(index, subPixelPosition)) { - if (fast_glyph_data[index]) { - delete fast_glyph_data[index]; - fast_glyph_data[index] = 0; - if (fast_glyph_count > 0) - --fast_glyph_count; - } - } else { - delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition)); - } -} - -void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph) -{ - if (useFastGlyphData(index, subPixelPosition)) { - if (!fast_glyph_data[index]) - ++fast_glyph_count; - fast_glyph_data[index] = glyph; - } else { - glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph); - } -} - -int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) -{ - lockFace(); - bool hsubpixel = true; - int vfactor = 1; - int load_flags = loadFlags(0, Format_A8, flags, hsubpixel, vfactor); - int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints); - unlockFace(); - return result; -} - -bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe) -{ - if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype)) - return false; - - // Increase the reference of this QFreetypeFace since one more QFontEngineFT - // will be using it - freetype->ref.ref(); - - default_load_flags = fe->default_load_flags; - default_hint_style = fe->default_hint_style; - antialias = fe->antialias; - transform = fe->transform; - embolden = fe->embolden; - obliquen = fe->obliquen; - subpixelType = fe->subpixelType; - lcdFilterType = fe->lcdFilterType; - embeddedbitmap = fe->embeddedbitmap; - - return true; -} - -QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const -{ - QFontDef fontDef(this->fontDef); - fontDef.pixelSize = pixelSize; - QFontEngineFT *fe = new QFontEngineFT(fontDef); - if (!fe->initFromFontEngine(this)) { - delete fe; - return 0; - } else { - return fe; - } -} - -Qt::HANDLE QFontEngineFT::handle() const -{ - return non_locked_face(); -} - -QT_END_NAMESPACE - -#endif // QT_NO_FREETYPE diff --git a/src/platformsupport/fontdatabases/basic/qfontengine_ft_p.h b/src/platformsupport/fontdatabases/basic/qfontengine_ft_p.h deleted file mode 100644 index 3b751eae3e..0000000000 --- a/src/platformsupport/fontdatabases/basic/qfontengine_ft_p.h +++ /dev/null @@ -1,360 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ -#ifndef QFONTENGINE_FT_P_H -#define QFONTENGINE_FT_P_H -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/qfontengine_p.h" - -#ifndef QT_NO_FREETYPE - -#include -#include FT_FREETYPE_H - - -#ifndef Q_OS_WIN -#include -#endif - -#include - -QT_BEGIN_NAMESPACE - -class QFontEngineFTRawFont; -class QFontconfigDatabase; - -/* - * This class represents one font file on disk (like Arial.ttf) and is shared between all the font engines - * that show this font file (at different pixel sizes). - */ -class QFreetypeFace -{ -public: - void computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor); - QFontEngine::Properties properties() const; - bool getSfntTable(uint tag, uchar *buffer, uint *length) const; - - static QFreetypeFace *getFace(const QFontEngine::FaceId &face_id, - const QByteArray &fontData = QByteArray()); - void release(const QFontEngine::FaceId &face_id); - - // locks the struct for usage. Any read/write operations require locking. - void lock() - { - _lock.lock(); - } - void unlock() - { - _lock.unlock(); - } - - FT_Face face; - int xsize; // 26.6 - int ysize; // 26.6 - FT_Matrix matrix; - FT_CharMap unicode_map; - FT_CharMap symbol_map; - - enum { cmapCacheSize = 0x200 }; - glyph_t cmapCache[cmapCacheSize]; - - int fsType() const; - - int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints); - - 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); - static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path); - -private: - friend class QFontEngineFT; - friend class QtFreetypeData; - friend struct QScopedPointerDeleter; - QFreetypeFace() : _lock(QMutex::Recursive) {} - ~QFreetypeFace() {} - void cleanup(); - QAtomicInt ref; - QMutex _lock; - QByteArray fontData; - - QFontEngine::Holder hbFace; -}; - -class QFontEngineFT : public QFontEngine -{ -public: - - /* we don't cache glyphs that are too large anyway, so we can make this struct rather small */ - struct Glyph { - ~Glyph(); - short linearAdvance; - unsigned char width; - unsigned char height; - short x; - short y; - short advance; - signed char format; - uchar *data; - }; - - struct GlyphInfo { - int linearAdvance; - unsigned short width; - unsigned short height; - short x; - short y; - short xOff; - short yOff; - }; - - struct GlyphAndSubPixelPosition - { - GlyphAndSubPixelPosition(glyph_t g, QFixed spp) : glyph(g), subPixelPosition(spp) {} - - bool operator==(const GlyphAndSubPixelPosition &other) const - { - return glyph == other.glyph && subPixelPosition == other.subPixelPosition; - } - - glyph_t glyph; - QFixed subPixelPosition; - }; - - struct QGlyphSet - { - QGlyphSet(); - ~QGlyphSet(); - FT_Matrix transformationMatrix; - bool outline_drawing; - - void removeGlyphFromCache(glyph_t index, QFixed subPixelPosition); - void clear(); - inline bool useFastGlyphData(glyph_t index, QFixed subPixelPosition) const { - return (index < 256 && subPixelPosition == 0); - } - inline Glyph *getGlyph(glyph_t index, QFixed subPixelPosition = 0) const; - void setGlyph(glyph_t index, QFixed spp, Glyph *glyph); - - inline bool isGlyphMissing(glyph_t index) const { return missing_glyphs.contains(index); } - inline void setGlyphMissing(glyph_t index) const { missing_glyphs.insert(index); } -private: - mutable QHash glyph_data; // maps from glyph index to glyph data - mutable QSet missing_glyphs; - mutable Glyph *fast_glyph_data[256]; // for fast lookup of glyphs < 256 - mutable int fast_glyph_count; - }; - - QFontEngine::FaceId faceId() const Q_DECL_OVERRIDE; - QFontEngine::Properties properties() const Q_DECL_OVERRIDE; - QFixed emSquareSize() const Q_DECL_OVERRIDE; - bool supportsSubPixelPositions() const Q_DECL_OVERRIDE - { - return default_hint_style == HintLight || - default_hint_style == HintNone; - } - - bool getSfntTableData(uint tag, uchar *buffer, uint *length) const Q_DECL_OVERRIDE; - int synthesized() const Q_DECL_OVERRIDE; - - QFixed ascent() const Q_DECL_OVERRIDE; - QFixed capHeight() const Q_DECL_OVERRIDE; - QFixed descent() const Q_DECL_OVERRIDE; - QFixed leading() const Q_DECL_OVERRIDE; - QFixed xHeight() const Q_DECL_OVERRIDE; - QFixed averageCharWidth() const Q_DECL_OVERRIDE; - - qreal maxCharWidth() const Q_DECL_OVERRIDE; - QFixed lineThickness() const Q_DECL_OVERRIDE; - QFixed underlinePosition() const Q_DECL_OVERRIDE; - - glyph_t glyphIndex(uint ucs4) const Q_DECL_OVERRIDE; - void doKerning(QGlyphLayout *, ShaperFlags) const Q_DECL_OVERRIDE; - - void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) Q_DECL_OVERRIDE; - - bool supportsTransformation(const QTransform &transform) const Q_DECL_OVERRIDE; - - void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, - QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; - void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, - QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; - - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const Q_DECL_OVERRIDE; - - glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) Q_DECL_OVERRIDE; - glyph_metrics_t boundingBox(glyph_t glyph) Q_DECL_OVERRIDE; - glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix) Q_DECL_OVERRIDE; - - void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags flags) const Q_DECL_OVERRIDE; - QImage alphaMapForGlyph(glyph_t g) Q_DECL_OVERRIDE { return alphaMapForGlyph(g, 0); } - QImage alphaMapForGlyph(glyph_t, QFixed) Q_DECL_OVERRIDE; - QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; - QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; - QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; - glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, - QFixed subPixelPosition, - const QTransform &matrix, - QFontEngine::GlyphFormat format) Q_DECL_OVERRIDE; - QImage *lockedAlphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, - GlyphFormat neededFormat, const QTransform &t, - QPoint *offset) Q_DECL_OVERRIDE; - bool hasInternalCaching() const Q_DECL_OVERRIDE { return cacheEnabled; } - void unlockAlphaMapForGlyph() Q_DECL_OVERRIDE; - bool expectsGammaCorrectedBlending() const Q_DECL_OVERRIDE; - - void removeGlyphFromCache(glyph_t glyph) Q_DECL_OVERRIDE; - int glyphMargin(QFontEngine::GlyphFormat /* format */) Q_DECL_OVERRIDE { return 0; } - - int glyphCount() const Q_DECL_OVERRIDE; - - enum Scaling { - Scaled, - Unscaled - }; - FT_Face lockFace(Scaling scale = Scaled) const; - void unlockFace() const; - - FT_Face non_locked_face() const; - - inline bool drawAntialiased() const { return antialias; } - inline bool invalid() const { return xsize == 0 && ysize == 0; } - inline bool isBitmapFont() const { return defaultFormat == Format_Mono; } - inline bool isScalableBitmap() const { return freetype->isScalableBitmap(); } - - inline Glyph *loadGlyph(uint glyph, QFixed subPixelPosition, GlyphFormat format = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const - { return loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, subPixelPosition, format, fetchMetricsOnly, disableOutlineDrawing); } - Glyph *loadGlyph(QGlyphSet *set, uint glyph, QFixed subPixelPosition, GlyphFormat = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const; - Glyph *loadGlyphFor(glyph_t g, QFixed subPixelPosition, GlyphFormat format, const QTransform &t, bool fetchBoundingBox = false, bool disableOutlineDrawing = false); - - QGlyphSet *loadGlyphSet(const QTransform &matrix); - - QFontEngineFT(const QFontDef &fd); - virtual ~QFontEngineFT(); - - bool init(FaceId faceId, bool antiaalias, GlyphFormat defaultFormat = Format_None, - const QByteArray &fontData = QByteArray()); - bool init(FaceId faceId, bool antialias, GlyphFormat format, - QFreetypeFace *freetypeFace); - - int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) Q_DECL_OVERRIDE; - - void setQtDefaultHintStyle(QFont::HintingPreference hintingPreference); - void setDefaultHintStyle(HintStyle style) Q_DECL_OVERRIDE; - - QFontEngine *cloneWithSize(qreal pixelSize) const Q_DECL_OVERRIDE; - Qt::HANDLE handle() const Q_DECL_OVERRIDE; - bool initFromFontEngine(const QFontEngineFT *fontEngine); - - HintStyle defaultHintStyle() const { return default_hint_style; } -protected: - - QFreetypeFace *freetype; - mutable int default_load_flags; - HintStyle default_hint_style; - bool antialias; - bool transform; - bool embolden; - bool obliquen; - SubpixelAntialiasingType subpixelType; - int lcdFilterType; - bool embeddedbitmap; - bool cacheEnabled; - bool forceAutoHint; - bool stemDarkeningDriver; - -private: - friend class QFontEngineFTRawFont; - friend class QFontconfigDatabase; - friend class QBasicFontDatabase; - friend class QCoreTextFontDatabase; - friend class QFontEngineMultiFontConfig; - - int loadFlags(QGlyphSet *set, GlyphFormat format, int flags, bool &hsubpixel, int &vfactor) const; - bool shouldUseDesignMetrics(ShaperFlags flags) const; - QFixed scaledBitmapMetrics(QFixed m) const; - glyph_metrics_t scaledBitmapMetrics(const glyph_metrics_t &m) const; - - GlyphFormat defaultFormat; - FT_Matrix matrix; - - QList transformedGlyphSets; - mutable QGlyphSet defaultGlyphSet; - - QFontEngine::FaceId face_id; - - int xsize; - int ysize; - - QFixed line_thickness; - QFixed underline_position; - - FT_Size_Metrics metrics; - mutable bool kerning_pairs_loaded; - QFixed scalableBitmapScaleFactor; -}; - -inline uint qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g) -{ - return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt(); -} - -inline QFontEngineFT::Glyph *QFontEngineFT::QGlyphSet::getGlyph(glyph_t index, QFixed subPixelPosition) const -{ - if (useFastGlyphData(index, subPixelPosition)) - return fast_glyph_data[index]; - return glyph_data.value(GlyphAndSubPixelPosition(index, subPixelPosition)); -} - -extern FT_Library qt_getFreetype(); - -QT_END_NAMESPACE - -#endif // QT_NO_FREETYPE - -#endif // QFONTENGINE_FT_P_H diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 2c5ce3e87d..4464e93bbd 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -692,7 +692,7 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr) QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) { - QFontEngineFT *engine = static_cast(QBasicFontDatabase::fontEngine(fontData, pixelSize, hintingPreference)); + QFontEngineFT *engine = static_cast(QFreeTypeFontDatabase::fontEngine(fontData, pixelSize, hintingPreference)); if (engine == 0) return 0; @@ -844,7 +844,7 @@ QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData, QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const { - QString resolved = QBasicFontDatabase::resolveFontFamilyAlias(family); + QString resolved = QFreeTypeFontDatabase::resolveFontFamilyAlias(family); if (!resolved.isEmpty() && resolved != family) return resolved; FcPattern *pattern = FcPatternCreate(); diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h index f7e3172b65..6a3261de30 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h @@ -52,13 +52,13 @@ // #include -#include +#include QT_BEGIN_NAMESPACE class QFontEngineFT; -class QFontconfigDatabase : public QBasicFontDatabase +class QFontconfigDatabase : public QFreeTypeFontDatabase { public: void populateFontDatabase() Q_DECL_OVERRIDE; diff --git a/src/platformsupport/fontdatabases/fontdatabases.pro b/src/platformsupport/fontdatabases/fontdatabases.pro index 49dead4668..d2726d08a0 100644 --- a/src/platformsupport/fontdatabases/fontdatabases.pro +++ b/src/platformsupport/fontdatabases/fontdatabases.pro @@ -11,7 +11,7 @@ darwin { include($$PWD/mac/coretext.pri) } else { qtConfig(freetype) { - include($$PWD/basic/basic.pri) + include($$PWD/freetype/freetype.pri) } unix { diff --git a/src/platformsupport/fontdatabases/freetype/freetype.pri b/src/platformsupport/fontdatabases/freetype/freetype.pri new file mode 100644 index 0000000000..7bda687ef4 --- /dev/null +++ b/src/platformsupport/fontdatabases/freetype/freetype.pri @@ -0,0 +1,9 @@ +HEADERS += \ + $$PWD/qfreetypefontdatabase_p.h \ + $$PWD/qfontengine_ft_p.h + +SOURCES += \ + $$PWD/qfreetypefontdatabase.cpp \ + $$PWD/qfontengine_ft.cpp + +QMAKE_USE_PRIVATE += freetype diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp new file mode 100644 index 0000000000..03e72546eb --- /dev/null +++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp @@ -0,0 +1,2214 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#include "qdir.h" +#include "qmetatype.h" +#include "qtextstream.h" +#include "qvariant.h" +#include "qfontengine_ft_p.h" +#include "private/qimage_p.h" +#include + +#ifndef QT_NO_FREETYPE + +#include "qfile.h" +#include "qfileinfo.h" +#include +#include "qthreadstorage.h" +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_SYNTHESIS_H +#include FT_TRUETYPE_TABLES_H +#include FT_TYPE1_TABLES_H +#include FT_GLYPH_H +#include FT_MODULE_H + +#if defined(FT_LCD_FILTER_H) +#include FT_LCD_FILTER_H +#define QT_USE_FREETYPE_LCDFILTER +#endif + +#if defined(FT_CONFIG_OPTIONS_H) +#include FT_CONFIG_OPTIONS_H +#endif + +#if defined(FT_FONT_FORMATS_H) +#include FT_FONT_FORMATS_H +#endif + +#ifdef QT_LINUXBASE +#include FT_ERRORS_H +#endif + +#if !defined(QT_MAX_CACHED_GLYPH_SIZE) +# define QT_MAX_CACHED_GLYPH_SIZE 64 +#endif + +QT_BEGIN_NAMESPACE + +#define FLOOR(x) ((x) & -64) +#define CEIL(x) (((x)+63) & -64) +#define TRUNC(x) ((x) >> 6) +#define ROUND(x) (((x)+32) & -64) + +static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length) +{ + FT_Face face = (FT_Face)user_data; + + bool result = false; + if (FT_IS_SFNT(face)) { + FT_ULong len = *length; + result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok; + *length = len; + Q_ASSERT(!result || int(*length) > 0); + } + + return result; +} + +static QFontEngineFT::Glyph emptyGlyph = {0, 0, 0, 0, 0, 0, 0, 0}; + +static const QFontEngine::HintStyle ftInitialDefaultHintStyle = +#ifdef Q_OS_WIN + QFontEngineFT::HintFull; +#else + QFontEngineFT::HintNone; +#endif + +// -------------------------- Freetype support ------------------------------ + +class QtFreetypeData +{ +public: + QtFreetypeData() + : library(0) + { } + ~QtFreetypeData(); + + FT_Library library; + QHash faces; +}; + +QtFreetypeData::~QtFreetypeData() +{ + for (QHash::ConstIterator iter = faces.cbegin(); iter != faces.cend(); ++iter) + iter.value()->cleanup(); + faces.clear(); + FT_Done_FreeType(library); + library = 0; +} + +#ifdef QT_NO_THREAD +Q_GLOBAL_STATIC(QtFreetypeData, theFreetypeData) + +QtFreetypeData *qt_getFreetypeData() +{ + return theFreetypeData(); +} +#else +Q_GLOBAL_STATIC(QThreadStorage, theFreetypeData) + +QtFreetypeData *qt_getFreetypeData() +{ + QtFreetypeData *&freetypeData = theFreetypeData()->localData(); + if (!freetypeData) + freetypeData = new QtFreetypeData; + if (!freetypeData->library) { + FT_Init_FreeType(&freetypeData->library); +#if defined(FT_FONT_FORMATS_H) + // Freetype defaults to disabling stem-darkening on CFF, we re-enable it. + FT_Bool no_darkening = false; + FT_Property_Set(freetypeData->library, "cff", "no-stem-darkening", &no_darkening); +#endif + } + return freetypeData; +} +#endif + +FT_Library qt_getFreetype() +{ + QtFreetypeData *freetypeData = qt_getFreetypeData(); + Q_ASSERT(freetypeData->library); + return freetypeData->library; +} + +int QFreetypeFace::fsType() const +{ + int fsType = 0; + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); + if (os2) + fsType = os2->fsType; + return fsType; +} + +int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) +{ + if (int error = FT_Load_Glyph(face, glyph, flags)) + return error; + + if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + return Err_Invalid_SubTable; + + *nPoints = face->glyph->outline.n_points; + if (!(*nPoints)) + return Err_Ok; + + if (point > *nPoints) + return Err_Invalid_SubTable; + + *xpos = QFixed::fromFixed(face->glyph->outline.points[point].x); + *ypos = QFixed::fromFixed(face->glyph->outline.points[point].y); + + return Err_Ok; +} + +bool QFreetypeFace::isScalableBitmap() const +{ +#ifdef FT_HAS_COLOR + return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face); +#else + return false; +#endif +} + +extern QByteArray qt_fontdata_from_index(int); + +/* + * One font file can contain more than one font (bold/italic for example) + * find the right one and return it. + * + * Returns the freetype face or 0 in case of an empty file or any other problems + * (like not being able to open the file) + */ +QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, + const QByteArray &fontData) +{ + if (face_id.filename.isEmpty() && fontData.isEmpty()) + return 0; + + QtFreetypeData *freetypeData = qt_getFreetypeData(); + + QFreetypeFace *freetype = freetypeData->faces.value(face_id, 0); + if (freetype) { + freetype->ref.ref(); + } else { + QScopedPointer newFreetype(new QFreetypeFace); + FT_Face face; + if (!face_id.filename.isEmpty()) { + QString fileName = QFile::decodeName(face_id.filename); + if (face_id.filename.startsWith(":qmemoryfonts/")) { + // from qfontdatabase.cpp + QByteArray idx = face_id.filename; + idx.remove(0, 14); // remove ':qmemoryfonts/' + bool ok = false; + newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok)); + if (!ok) + newFreetype->fontData = QByteArray(); + } else if (!QFileInfo(fileName).isNativePath()) { + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) { + return 0; + } + newFreetype->fontData = file.readAll(); + } + } else { + newFreetype->fontData = fontData; + } + if (!newFreetype->fontData.isEmpty()) { + if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) { + return 0; + } + } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) { + return 0; + } + newFreetype->face = face; + + newFreetype->ref.store(1); + newFreetype->xsize = 0; + newFreetype->ysize = 0; + newFreetype->matrix.xx = 0x10000; + newFreetype->matrix.yy = 0x10000; + newFreetype->matrix.xy = 0; + newFreetype->matrix.yx = 0; + newFreetype->unicode_map = 0; + newFreetype->symbol_map = 0; + + memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache)); + + for (int i = 0; i < newFreetype->face->num_charmaps; ++i) { + FT_CharMap cm = newFreetype->face->charmaps[i]; + switch(cm->encoding) { + case FT_ENCODING_UNICODE: + newFreetype->unicode_map = cm; + break; + case FT_ENCODING_APPLE_ROMAN: + case FT_ENCODING_ADOBE_LATIN_1: + if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE) + newFreetype->unicode_map = cm; + break; + case FT_ENCODING_ADOBE_CUSTOM: + case FT_ENCODING_MS_SYMBOL: + if (!newFreetype->symbol_map) + newFreetype->symbol_map = cm; + break; + default: + break; + } + } + + if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1) + 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); + QT_TRY { + freetypeData->faces.insert(face_id, newFreetype.data()); + } QT_CATCH(...) { + newFreetype.take()->release(face_id); + // we could return null in principle instead of throwing + QT_RETHROW; + } + freetype = newFreetype.take(); + } + return freetype; +} + +void QFreetypeFace::cleanup() +{ + hbFace.reset(); + FT_Done_Face(face); + face = 0; +} + +void QFreetypeFace::release(const QFontEngine::FaceId &face_id) +{ + if (!ref.deref()) { + if (face) { + QtFreetypeData *freetypeData = qt_getFreetypeData(); + + cleanup(); + + auto it = freetypeData->faces.constFind(face_id); + if (it != freetypeData->faces.constEnd()) + freetypeData->faces.erase(it); + + if (freetypeData->faces.isEmpty()) { + FT_Done_FreeType(freetypeData->library); + freetypeData->library = 0; + } + } + + delete this; + } +} + + +void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor) +{ + *ysize = qRound(fontDef.pixelSize * 64); + *xsize = *ysize * fontDef.stretch / 100; + *scalableBitmapScaleFactor = 1; + *outline_drawing = false; + + if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) { + int best = 0; + if (!isScalableBitmap()) { + /* + * Bitmap only faces must match exactly, so find the closest + * one (height dominant search) + */ + for (int i = 1; i < face->num_fixed_sizes; i++) { + if (qAbs(*ysize - face->available_sizes[i].y_ppem) < + qAbs(*ysize - face->available_sizes[best].y_ppem) || + (qAbs(*ysize - face->available_sizes[i].y_ppem) == + qAbs(*ysize - face->available_sizes[best].y_ppem) && + qAbs(*xsize - face->available_sizes[i].x_ppem) < + qAbs(*xsize - face->available_sizes[best].x_ppem))) { + best = i; + } + } + } else { + // Select the shortest bitmap strike whose height is larger than the desired height + for (int i = 1; i < face->num_fixed_sizes; i++) { + if (face->available_sizes[i].y_ppem < *ysize) { + if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem) + best = i; + } else if (face->available_sizes[best].y_ppem < *ysize) { + best = i; + } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) { + best = i; + } + } + } + + // According to freetype documentation we must use FT_Select_Size + // to make sure we can select the desired bitmap strike index + if (FT_Select_Size(face, best) == 0) { + if (isScalableBitmap()) + *scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / face->available_sizes[best].height); + *xsize = face->available_sizes[best].x_ppem; + *ysize = face->available_sizes[best].y_ppem; + } else { + *xsize = *ysize = 0; + } + } else { + *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6)); + } +} + +QFontEngine::Properties QFreetypeFace::properties() const +{ + QFontEngine::Properties p; + p.postscriptName = FT_Get_Postscript_Name(face); + PS_FontInfoRec font_info; + if (FT_Get_PS_Font_Info(face, &font_info) == 0) + p.copyright = font_info.notice; + if (FT_IS_SCALABLE(face)) { + p.ascent = face->ascender; + p.descent = -face->descender; + p.leading = face->height - face->ascender + face->descender; + p.emSquare = face->units_per_EM; + p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax, + face->bbox.xMax - face->bbox.xMin, + face->bbox.yMax - face->bbox.yMin); + } else { + p.ascent = QFixed::fromFixed(face->size->metrics.ascender); + p.descent = QFixed::fromFixed(-face->size->metrics.descender); + p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender); + p.emSquare = face->size->metrics.y_ppem; +// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.); + p.boundingBox = QRectF(0, -p.ascent.toReal(), + face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() ); + } + p.italicAngle = 0; + p.capHeight = p.ascent; + p.lineWidth = face->underline_thickness; + + return p; +} + +bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const +{ + return ft_getSfntTable(face, tag, buffer, length); +} + +/* Some fonts (such as MingLiu rely on hinting to scale different + components to their correct sizes. While this is really broken (it + should be done in the component glyph itself, not the hinter) we + will have to live with it. + + This means we can not use FT_LOAD_NO_HINTING to get the glyph + outline. All we can do is to load the unscaled glyph and scale it + down manually when required. +*/ +static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale) +{ + x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM); + y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM); + FT_Vector *p = g->outline.points; + const FT_Vector *e = p + g->outline.n_points; + while (p < e) { + p->x = FT_MulFix(p->x, x_scale); + p->y = FT_MulFix(p->y, y_scale); + ++p; + } +} + +#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug +void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale) +{ + const qreal factor = 1/64.; + scaleOutline(face, g, x_scale, y_scale); + + QPointF cp = point.toPointF(); + + // convert the outline to a painter path + int i = 0; + for (int j = 0; j < g->outline.n_contours; ++j) { + int last_point = g->outline.contours[j]; + GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point; + QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); + if (!(g->outline.tags[i] & 1)) { // start point is not on curve: + if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve: + GLYPH2PATH_DEBUG() << " start and end point are not on curve"; + start = (QPointF(g->outline.points[last_point].x*factor, + -g->outline.points[last_point].y*factor) + start) / 2.0; + } else { + GLYPH2PATH_DEBUG() << " end point is on curve, start is not"; + start = QPointF(g->outline.points[last_point].x*factor, + -g->outline.points[last_point].y*factor); + } + --i; // to use original start point as control point below + } + start += cp; + GLYPH2PATH_DEBUG() << " start at" << start; + + path->moveTo(start); + QPointF c[4]; + c[0] = start; + int n = 1; + while (i < last_point) { + ++i; + c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); + GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i] + << ": on curve =" << (bool)(g->outline.tags[i] & 1); + ++n; + switch (g->outline.tags[i] & 3) { + case 2: + // cubic bezier element + if (n < 4) + continue; + c[3] = (c[3] + c[2])/2; + --i; + break; + case 0: + // quadratic bezier element + if (n < 3) + continue; + c[3] = (c[1] + c[2])/2; + c[2] = (2*c[1] + c[3])/3; + c[1] = (2*c[1] + c[0])/3; + --i; + break; + case 1: + case 3: + if (n == 2) { + GLYPH2PATH_DEBUG() << " lineTo" << c[1]; + path->lineTo(c[1]); + c[0] = c[1]; + n = 1; + continue; + } else if (n == 3) { + c[3] = c[2]; + c[2] = (2*c[1] + c[3])/3; + c[1] = (2*c[1] + c[0])/3; + } + break; + } + GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3]; + path->cubicTo(c[1], c[2], c[3]); + c[0] = c[3]; + n = 1; + } + + if (n == 1) { + GLYPH2PATH_DEBUG() << " closeSubpath"; + path->closeSubpath(); + } else { + c[3] = start; + if (n == 2) { + c[2] = (2*c[1] + c[3])/3; + c[1] = (2*c[1] + c[0])/3; + } + GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3]; + path->cubicTo(c[1], c[2], c[3]); + } + ++i; + } +} + +extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path); + +void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path) +{ + if (slot->format != FT_GLYPH_FORMAT_BITMAP + || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO) + return; + + QPointF cp = point.toPointF(); + qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY), + slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path); +} + +QFontEngineFT::Glyph::~Glyph() +{ + delete [] data; +} + +struct LcdFilterDummy +{ + static inline void filterPixel(uchar &, uchar &, uchar &) + {} +}; + +struct LcdFilterLegacy +{ + static inline void filterPixel(uchar &red, uchar &green, uchar &blue) + { + uint r = red, g = green, b = blue; + // intra-pixel filter used by the legacy filter (adopted from _ft_lcd_filter_legacy) + red = (r * uint(65538 * 9/13) + g * uint(65538 * 1/6) + b * uint(65538 * 1/13)) / 65536; + green = (r * uint(65538 * 3/13) + g * uint(65538 * 4/6) + b * uint(65538 * 3/13)) / 65536; + blue = (r * uint(65538 * 1/13) + g * uint(65538 * 1/6) + b * uint(65538 * 9/13)) / 65536; + } +}; + +template +static void convertRGBToARGB_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr) +{ + const int offs = bgr ? -1 : 1; + const int w = width * 3; + while (height--) { + uint *dd = dst; + for (int x = 0; x < w; x += 3) { + uchar red = src[x + 1 - offs]; + uchar green = src[x + 1]; + uchar blue = src[x + 1 + offs]; + LcdFilter::filterPixel(red, green, blue); + *dd++ = (0xFF << 24) | (red << 16) | (green << 8) | blue; + } + dst += width; + src += src_pitch; + } +} + +static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) +{ + if (!legacyFilter) + convertRGBToARGB_helper(src, dst, width, height, src_pitch, bgr); + else + convertRGBToARGB_helper(src, dst, width, height, src_pitch, bgr); +} + +template +static void convertRGBToARGB_V_helper(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr) +{ + const int offs = bgr ? -src_pitch : src_pitch; + while (height--) { + for (int x = 0; x < width; x++) { + uchar red = src[x + src_pitch - offs]; + uchar green = src[x + src_pitch]; + uchar blue = src[x + src_pitch + offs]; + LcdFilter::filterPixel(red, green, blue); + *dst++ = (0XFF << 24) | (red << 16) | (green << 8) | blue; + } + src += 3*src_pitch; + } +} + +static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) +{ + if (!legacyFilter) + convertRGBToARGB_V_helper(src, dst, width, height, src_pitch, bgr); + else + convertRGBToARGB_V_helper(src, dst, width, height, src_pitch, bgr); +} + +static inline void convertGRAYToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch) +{ + while (height--) { + const uchar *p = src; + const uchar * const e = p + width; + while (p < e) { + uchar gray = *p++; + *dst++ = (0xFF << 24) | (gray << 16) | (gray << 8) | gray; + } + src += src_pitch; + } +} + +static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, int pitch) +{ + // convolute the bitmap with a triangle filter to get rid of color fringes + // If we take account for a gamma value of 2, we end up with + // weights of 1, 4, 9, 4, 1. We use an approximation of 1, 3, 8, 3, 1 here, + // as this nicely sums up to 16 :) + int h = height; + while (h--) { + dst[0] = dst[1] = 0; + // + for (int x = 2; x < width - 2; ++x) { + uint sum = src[x-2] + 3*src[x-1] + 8*src[x] + 3*src[x+1] + src[x+2]; + dst[x] = (uchar) (sum >> 4); + } + dst[width - 2] = dst[width - 1] = 0; + src += pitch; + dst += pitch; + } +} + +QFontEngineFT::QFontEngineFT(const QFontDef &fd) + : QFontEngine(Freetype) +{ + fontDef = fd; + matrix.xx = 0x10000; + matrix.yy = 0x10000; + matrix.xy = 0; + matrix.yx = 0; + cache_cost = 100 * 1024; + kerning_pairs_loaded = false; + transform = false; + embolden = false; + obliquen = false; + antialias = true; + freetype = 0; + default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + default_hint_style = ftInitialDefaultHintStyle; + subpixelType = Subpixel_None; + lcdFilterType = 0; +#if defined(FT_LCD_FILTER_H) + lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT); +#endif + defaultFormat = Format_None; + embeddedbitmap = false; + const QByteArray env = qgetenv("QT_NO_FT_CACHE"); + cacheEnabled = env.isEmpty() || env.toInt() == 0; + m_subPixelPositionCount = 4; + forceAutoHint = false; + stemDarkeningDriver = false; +} + +QFontEngineFT::~QFontEngineFT() +{ + if (freetype) + freetype->release(face_id); +} + +bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, + const QByteArray &fontData) +{ + return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData)); +} + +static void dont_delete(void*) {} + +bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, + QFreetypeFace *freetypeFace) +{ + freetype = freetypeFace; + if (!freetype) { + xsize = 0; + ysize = 0; + return false; + } + defaultFormat = format; + this->antialias = antialias; + + if (!antialias) + glyphFormat = QFontEngine::Format_Mono; + else + glyphFormat = defaultFormat; + + face_id = faceId; + + symbol = freetype->symbol_map != 0; + 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 = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive)); + } + + freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor); + + FT_Face face = lockFace(); + + if (FT_IS_SCALABLE(face)) { + bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC); + if (fake_oblique) + obliquen = true; + FT_Set_Transform(face, &matrix, 0); + freetype->matrix = matrix; + // fake bold + if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face)) { + if (const TT_OS2 *os2 = reinterpret_cast(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) { + if (os2->usWeightClass < 750) + 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)); + } else { + // ad hoc algorithm + int score = fontDef.weight * fontDef.pixelSize; + line_thickness = score / 700; + // 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()) { + glyphFormat = defaultFormat = GlyphFormat::Format_ARGB; + cacheEnabled = false; + } + } + if (line_thickness < 1) + line_thickness = 1; + + metrics = face->size->metrics; + + /* + 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 + the metrics are retrieved from the bitmap strikes. + */ + if (FT_IS_SCALABLE(face)) { + for (int i = 0; i < face->num_fixed_sizes; ++i) { + if (xsize == face->available_sizes[i].x_ppem && ysize == face->available_sizes[i].y_ppem) { + face->face_flags &= ~FT_FACE_FLAG_SCALABLE; + + FT_Select_Size(face, i); + if (face->size->metrics.ascender + face->size->metrics.descender > 0) { + FT_Pos leading = metrics.height - metrics.ascender + metrics.descender; + metrics.ascender = face->size->metrics.ascender; + metrics.descender = face->size->metrics.descender; + if (metrics.descender > 0 + && QString::fromUtf8(face->family_name) == QLatin1String("Courier New")) { + metrics.descender *= -1; + } + metrics.height = metrics.ascender - metrics.descender + leading; + } + FT_Set_Char_Size(face, xsize, ysize, 0, 0); + + face->face_flags |= FT_FACE_FLAG_SCALABLE; + break; + } + } + } +#if defined(FT_FONT_FORMATS_H) + const char *fmt = FT_Get_Font_Format(face); + if (fmt && qstrncmp(fmt, "CFF", 4) == 0) { + FT_Bool no_stem_darkening = true; + FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening); + if (err == FT_Err_Ok) + stemDarkeningDriver = !no_stem_darkening; + else + stemDarkeningDriver = false; + } +#endif + + fontDef.styleName = QString::fromUtf8(face->style_name); + + if (!freetype->hbFace) { + faceData.user_data = face; + faceData.get_font_table = ft_getSfntTable; + (void)harfbuzzFace(); // populates face_ + freetype->hbFace = std::move(face_); + } else { + Q_ASSERT(!face_); + } + // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it + face_ = Holder(freetype->hbFace.get(), dont_delete); + + unlockFace(); + + fsType = freetype->fsType(); + return true; +} + +void QFontEngineFT::setQtDefaultHintStyle(QFont::HintingPreference hintingPreference) +{ + switch (hintingPreference) { + case QFont::PreferNoHinting: + setDefaultHintStyle(HintNone); + break; + case QFont::PreferFullHinting: + setDefaultHintStyle(HintFull); + break; + case QFont::PreferVerticalHinting: + setDefaultHintStyle(HintLight); + break; + case QFont::PreferDefaultHinting: + setDefaultHintStyle(ftInitialDefaultHintStyle); + break; + } +} + +void QFontEngineFT::setDefaultHintStyle(HintStyle style) +{ + default_hint_style = style; +} + +bool QFontEngineFT::expectsGammaCorrectedBlending() const +{ + return stemDarkeningDriver; +} + +int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags, + bool &hsubpixel, int &vfactor) const +{ + int load_flags = FT_LOAD_DEFAULT | default_load_flags; + int load_target = default_hint_style == HintLight + ? FT_LOAD_TARGET_LIGHT + : FT_LOAD_TARGET_NORMAL; + + if (format == Format_Mono) { + load_target = FT_LOAD_TARGET_MONO; + } else if (format == Format_A32) { + if (subpixelType == Subpixel_RGB || subpixelType == Subpixel_BGR) { + if (default_hint_style == HintFull) + load_target = FT_LOAD_TARGET_LCD; + hsubpixel = true; + } else if (subpixelType == Subpixel_VRGB || subpixelType == Subpixel_VBGR) { + if (default_hint_style == HintFull) + load_target = FT_LOAD_TARGET_LCD_V; + vfactor = 3; + } + } else if (format == Format_ARGB) { +#ifdef FT_LOAD_COLOR + load_flags |= FT_LOAD_COLOR; +#endif + } + + if (set && set->outline_drawing) + load_flags |= FT_LOAD_NO_BITMAP; + + if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing)) + load_flags |= FT_LOAD_NO_HINTING; + else + load_flags |= load_target; + + if (forceAutoHint) + load_flags |= FT_LOAD_FORCE_AUTOHINT; + + return load_flags; +} + +static inline bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info) +{ + // false if exceeds QFontEngineFT::Glyph metrics + return (short)(info.linearAdvance) != info.linearAdvance + || (uchar)(info.width) != info.width + || (uchar)(info.height) != info.height; +} + +static inline void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix) +{ + int l, r, t, b; + FT_Vector vector; + vector.x = *left; + vector.y = *top; + FT_Vector_Transform(&vector, matrix); + l = r = vector.x; + t = b = vector.y; + vector.x = *right; + vector.y = *top; + FT_Vector_Transform(&vector, matrix); + if (l > vector.x) l = vector.x; + if (r < vector.x) r = vector.x; + if (t < vector.y) t = vector.y; + if (b > vector.y) b = vector.y; + vector.x = *right; + vector.y = *bottom; + FT_Vector_Transform(&vector, matrix); + if (l > vector.x) l = vector.x; + if (r < vector.x) r = vector.x; + if (t < vector.y) t = vector.y; + if (b > vector.y) b = vector.y; + vector.x = *left; + vector.y = *bottom; + FT_Vector_Transform(&vector, matrix); + if (l > vector.x) l = vector.x; + if (r < vector.x) r = vector.x; + if (t < vector.y) t = vector.y; + if (b > vector.y) b = vector.y; + *left = l; + *right = r; + *top = t; + *bottom = b; +} + +QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, + QFixed subPixelPosition, + GlyphFormat format, + bool fetchMetricsOnly, + bool disableOutlineDrawing) const +{ +// Q_ASSERT(freetype->lock == 1); + + if (format == Format_None) + format = defaultFormat != Format_None ? defaultFormat : Format_Mono; + Q_ASSERT(format != Format_None); + + Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : 0; + if (g && g->format == format && (fetchMetricsOnly || g->data)) + return g; + + if (!g && set && set->isGlyphMissing(glyph)) + return &emptyGlyph; + + + FT_Face face = freetype->face; + + FT_Matrix matrix = freetype->matrix; + + FT_Vector v; + v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.value()); + v.y = 0; + FT_Set_Transform(face, &matrix, &v); + + bool hsubpixel = false; + int vfactor = 1; + int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor); + + bool transform = matrix.xx != 0x10000 + || matrix.yy != 0x10000 + || matrix.xy != 0 + || matrix.yx != 0; + + if (transform || (format != Format_Mono && !isScalableBitmap())) + load_flags |= FT_LOAD_NO_BITMAP; + + FT_Error err = FT_Load_Glyph(face, glyph, load_flags); + if (err && (load_flags & FT_LOAD_NO_BITMAP)) { + load_flags &= ~FT_LOAD_NO_BITMAP; + err = FT_Load_Glyph(face, glyph, load_flags); + } + if (err == FT_Err_Too_Few_Arguments) { + // this is an error in the bytecode interpreter, just try to run without it + load_flags |= FT_LOAD_FORCE_AUTOHINT; + err = FT_Load_Glyph(face, glyph, load_flags); + } else if (err == FT_Err_Execution_Too_Long) { + // This is an error in the bytecode, probably a web font made by someone who + // didn't test bytecode hinting at all so disable for it for all glyphs. + qWarning("load glyph failed due to broken hinting bytecode in font, switching to auto hinting"); + default_load_flags |= FT_LOAD_FORCE_AUTOHINT; + load_flags |= FT_LOAD_FORCE_AUTOHINT; + err = FT_Load_Glyph(face, glyph, load_flags); + } + if (err != FT_Err_Ok) { + qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph); + if (set) + set->setGlyphMissing(glyph); + return &emptyGlyph; + } + + FT_GlyphSlot slot = face->glyph; + + if (embolden) + FT_GlyphSlot_Embolden(slot); + if (obliquen) { + FT_GlyphSlot_Oblique(slot); + + // While Embolden alters the metrics of the slot, oblique does not, so we need + // to fix this ourselves. + transform = true; + FT_Matrix m; + m.xx = 0x10000; + m.yx = 0x0; + m.xy = 0x6000; + m.yy = 0x10000; + + FT_Matrix_Multiply(&m, &matrix); + } + + GlyphInfo info; + info.linearAdvance = slot->linearHoriAdvance >> 10; + info.xOff = TRUNC(ROUND(slot->advance.x)); + info.yOff = 0; + + if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) { + int left = slot->metrics.horiBearingX; + int right = slot->metrics.horiBearingX + slot->metrics.width; + int top = slot->metrics.horiBearingY; + int bottom = slot->metrics.horiBearingY - slot->metrics.height; + + if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP) + transformBoundingBox(&left, &top, &right, &bottom, &matrix); + + left = FLOOR(left); + right = CEIL(right); + bottom = FLOOR(bottom); + top = CEIL(top); + + info.x = TRUNC(left); + info.y = TRUNC(top); + info.width = TRUNC(right - left); + info.height = TRUNC(top - bottom); + + // If any of the metrics are too large to fit, don't cache them + if (areMetricsTooLarge(info)) + return 0; + + g = new Glyph; + g->data = 0; + g->linearAdvance = info.linearAdvance; + g->width = info.width; + g->height = info.height; + g->x = info.x; + g->y = info.y; + g->advance = info.xOff; + g->format = format; + + if (set) + set->setGlyph(glyph, subPixelPosition, g); + + return g; + } + + int glyph_buffer_size = 0; + QScopedArrayPointer glyph_buffer; +#if defined(QT_USE_FREETYPE_LCDFILTER) + bool useFreetypeRenderGlyph = false; + if (slot->format == FT_GLYPH_FORMAT_OUTLINE && (hsubpixel || vfactor != 1)) { + err = FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType); + if (err == FT_Err_Ok) + useFreetypeRenderGlyph = true; + } + + if (useFreetypeRenderGlyph) { + err = FT_Render_Glyph(slot, hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V); + + if (err != FT_Err_Ok) + qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph); + + FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE); + + info.height = slot->bitmap.rows / vfactor; + info.width = hsubpixel ? slot->bitmap.width / 3 : slot->bitmap.width; + info.x = slot->bitmap_left; + info.y = slot->bitmap_top; + + glyph_buffer_size = info.width * info.height * 4; + glyph_buffer.reset(new uchar[glyph_buffer_size]); + + if (hsubpixel) + convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB, false); + else if (vfactor != 1) + convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB, false); + } else +#endif + { + int left = slot->metrics.horiBearingX; + int right = slot->metrics.horiBearingX + slot->metrics.width; + int top = slot->metrics.horiBearingY; + int bottom = slot->metrics.horiBearingY - slot->metrics.height; + if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP) + transformBoundingBox(&left, &top, &right, &bottom, &matrix); + left = FLOOR(left); + right = CEIL(right); + bottom = FLOOR(bottom); + top = CEIL(top); + + int hpixels = TRUNC(right - left); + // subpixel position requires one more pixel + if (subPixelPosition > 0 && format != Format_Mono) + hpixels++; + + if (hsubpixel) + hpixels = hpixels*3 + 8; + info.width = hpixels; + info.height = TRUNC(top - bottom); + info.x = TRUNC(left); + info.y = TRUNC(top); + if (hsubpixel) { + info.width /= 3; + info.x -= 1; + } + + // If any of the metrics are too large to fit, don't cache them + if (areMetricsTooLarge(info)) + return 0; + + int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 : + (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4)); + if (glyph_buffer_size < pitch * info.height) { + glyph_buffer_size = pitch * info.height; + glyph_buffer.reset(new uchar[glyph_buffer_size]); + memset(glyph_buffer.data(), 0, glyph_buffer_size); + } + + if (slot->format == FT_GLYPH_FORMAT_OUTLINE) { + FT_Bitmap bitmap; + bitmap.rows = info.height*vfactor; + bitmap.width = hpixels; + bitmap.pitch = format == Format_Mono ? (((info.width + 31) & ~31) >> 3) : ((bitmap.width + 3) & ~3); + int bitmap_buffer_size = bitmap.rows * bitmap.pitch; + if (!hsubpixel && vfactor == 1 && format != Format_A32) { + Q_ASSERT(glyph_buffer_size <= bitmap_buffer_size); + bitmap.buffer = glyph_buffer.data(); + } else { + bitmap.buffer = new uchar[bitmap_buffer_size]; + memset(bitmap.buffer, 0, bitmap_buffer_size); + } + bitmap.pixel_mode = format == Format_Mono ? FT_PIXEL_MODE_MONO : FT_PIXEL_MODE_GRAY; + FT_Matrix matrix; + matrix.xx = (hsubpixel ? 3 : 1) << 16; + matrix.yy = vfactor << 16; + matrix.yx = matrix.xy = 0; + + FT_Outline_Transform(&slot->outline, &matrix); + FT_Outline_Translate (&slot->outline, (hsubpixel ? -3*left +(4<<6) : -left), -bottom*vfactor); + FT_Outline_Get_Bitmap(slot->library, &slot->outline, &bitmap); + if (hsubpixel) { + Q_ASSERT (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); + Q_ASSERT(antialias); + uchar *convoluted = new uchar[bitmap_buffer_size]; + bool useLegacyLcdFilter = false; +#if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H) + useLegacyLcdFilter = (lcdFilterType == FT_LCD_FILTER_LEGACY); +#endif + uchar *buffer = bitmap.buffer; + if (!useLegacyLcdFilter) { + convoluteBitmap(bitmap.buffer, convoluted, bitmap.width, info.height, bitmap.pitch); + buffer = convoluted; + } + convertRGBToARGB(buffer + 1, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_RGB, useLegacyLcdFilter); + delete [] convoluted; + } else if (vfactor != 1) { + convertRGBToARGB_V(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch, subpixelType != Subpixel_VRGB, true); + } else if (format == Format_A32 && bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { + convertGRAYToARGB(bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, bitmap.pitch); + } + + if (bitmap.buffer != glyph_buffer.data()) + delete [] bitmap.buffer; + } else if (slot->format == FT_GLYPH_FORMAT_BITMAP) { +#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500) + Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO || slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA); +#else + Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO); +#endif + uchar *src = slot->bitmap.buffer; + uchar *dst = glyph_buffer.data(); + int h = slot->bitmap.rows; + if (format == Format_Mono) { + int bytes = ((info.width + 7) & ~7) >> 3; + while (h--) { + memcpy (dst, src, bytes); + dst += pitch; + src += slot->bitmap.pitch; + } + } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + if (hsubpixel) { + while (h--) { + uint *dd = (uint *)dst; + *dd++ = 0; + for (int x = 0; x < static_cast(slot->bitmap.width); x++) { + uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); + *dd++ = a; + } + *dd++ = 0; + dst += pitch; + src += slot->bitmap.pitch; + } + } else if (vfactor != 1) { + while (h--) { + uint *dd = (uint *)dst; + for (int x = 0; x < static_cast(slot->bitmap.width); x++) { + uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); + *dd++ = a; + } + dst += pitch; + src += slot->bitmap.pitch; + } + } else { + while (h--) { + for (int x = 0; x < static_cast(slot->bitmap.width); x++) { + unsigned char a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00); + dst[x] = a; + } + dst += pitch; + src += slot->bitmap.pitch; + } + } + } +#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500) + else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) + { + while (h--) { +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + const quint32 *srcPixel = (const quint32 *)src; + quint32 *dstPixel = (quint32 *)dst; + for (int x = 0; x < static_cast(slot->bitmap.width); x++, srcPixel++, dstPixel++) { + const quint32 pixel = *srcPixel; + *dstPixel = qbswap(pixel); + } +#else + memcpy(dst, src, slot->bitmap.width * 4); +#endif + dst += slot->bitmap.pitch; + src += slot->bitmap.pitch; + } + info.width = info.linearAdvance = info.xOff = slot->bitmap.width; + info.height = slot->bitmap.rows; + info.x = slot->bitmap_left; + info.y = slot->bitmap_top; + } +#endif + } else { + qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format); + return 0; + } + } + + + if (!g) { + g = new Glyph; + g->data = 0; + } + + g->linearAdvance = info.linearAdvance; + g->width = info.width; + g->height = info.height; + g->x = info.x; + g->y = info.y; + g->advance = info.xOff; + g->format = format; + delete [] g->data; + g->data = glyph_buffer.take(); + + if (set) + set->setGlyph(glyph, subPixelPosition, g); + + return g; +} + +QFontEngine::FaceId QFontEngineFT::faceId() const +{ + return face_id; +} + +QFontEngine::Properties QFontEngineFT::properties() const +{ + Properties p = freetype->properties(); + if (p.postscriptName.isEmpty()) { + p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8()); + } + + return freetype->properties(); +} + +QFixed QFontEngineFT::emSquareSize() const +{ + if (FT_IS_SCALABLE(freetype->face)) + return freetype->face->units_per_EM; + else + return freetype->face->size->metrics.y_ppem; +} + +bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + return ft_getSfntTable(freetype->face, tag, buffer, length); +} + +int QFontEngineFT::synthesized() const +{ + int s = 0; + if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)) + s = SynthesizedItalic; + if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD)) + s |= SynthesizedBold; + if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face)) + s |= SynthesizedStretch; + return s; +} + +QFixed QFontEngineFT::ascent() const +{ + QFixed v = QFixed::fromFixed(metrics.ascender); + if (scalableBitmapScaleFactor != 1) + v *= scalableBitmapScaleFactor; + return v; +} + +QFixed QFontEngineFT::capHeight() const +{ + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->version >= 2) { + lockFace(); + QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale)); + unlockFace(); + return answer; + } + return calculatedCapHeight(); +} + +QFixed QFontEngineFT::descent() const +{ + QFixed v = QFixed::fromFixed(-metrics.descender); + if (scalableBitmapScaleFactor != 1) + v *= scalableBitmapScaleFactor; + return v; +} + +QFixed QFontEngineFT::leading() const +{ + QFixed v = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender); + if (scalableBitmapScaleFactor != 1) + v *= scalableBitmapScaleFactor; + return v; +} + +QFixed QFontEngineFT::xHeight() const +{ + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->sxHeight) { + lockFace(); + QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize(); + unlockFace(); + return answer; + } + + return QFontEngine::xHeight(); +} + +QFixed QFontEngineFT::averageCharWidth() const +{ + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->xAvgCharWidth) { + lockFace(); + QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize(); + unlockFace(); + return answer; + } + + return QFontEngine::averageCharWidth(); +} + +qreal QFontEngineFT::maxCharWidth() const +{ + QFixed max_advance = QFixed::fromFixed(metrics.max_advance); + if (scalableBitmapScaleFactor != 1) + max_advance *= scalableBitmapScaleFactor; + return max_advance.toReal(); +} + +QFixed QFontEngineFT::lineThickness() const +{ + return line_thickness; +} + +QFixed QFontEngineFT::underlinePosition() const +{ + return underline_position; +} + +void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const +{ + if (!kerning_pairs_loaded) { + kerning_pairs_loaded = true; + lockFace(); + if (freetype->face->size->metrics.x_ppem != 0) { + QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem); + unlockFace(); + const_cast(this)->loadKerningPairs(scalingFactor); + } else { + unlockFace(); + } + } + + if (shouldUseDesignMetrics(flags) && !(fontDef.styleStrategy & QFont::ForceIntegerMetrics)) + flags |= DesignMetrics; + else + flags &= ~DesignMetrics; + + QFontEngine::doKerning(g, flags); +} + +static inline FT_Matrix QTransformToFTMatrix(const QTransform &matrix) +{ + FT_Matrix m; + + m.xx = FT_Fixed(matrix.m11() * 65536); + m.xy = FT_Fixed(-matrix.m21() * 65536); + m.yx = FT_Fixed(-matrix.m12() * 65536); + m.yy = FT_Fixed(matrix.m22() * 65536); + + return m; +} + +QFontEngineFT::QGlyphSet *QFontEngineFT::loadGlyphSet(const QTransform &matrix) +{ + if (matrix.type() > QTransform::TxShear || !cacheEnabled) + return 0; + + // FT_Set_Transform only supports scalable fonts + if (!FT_IS_SCALABLE(freetype->face)) + return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet : Q_NULLPTR; + + FT_Matrix m = QTransformToFTMatrix(matrix); + + QGlyphSet *gs = 0; + + for (int i = 0; i < transformedGlyphSets.count(); ++i) { + const QGlyphSet &g = transformedGlyphSets.at(i); + if (g.transformationMatrix.xx == m.xx + && g.transformationMatrix.xy == m.xy + && g.transformationMatrix.yx == m.yx + && g.transformationMatrix.yy == m.yy) { + + // found a match, move it to the front + transformedGlyphSets.move(i, 0); + gs = &transformedGlyphSets[0]; + break; + } + } + + if (!gs) { + // don't cache more than 10 transformations + if (transformedGlyphSets.count() >= 10) { + transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0); + } else { + transformedGlyphSets.prepend(QGlyphSet()); + } + gs = &transformedGlyphSets[0]; + gs->clear(); + gs->transformationMatrix = m; + gs->outline_drawing = fontDef.pixelSize * fontDef.pixelSize * qAbs(matrix.det()) >= QT_MAX_CACHED_GLYPH_SIZE * QT_MAX_CACHED_GLYPH_SIZE; + } + Q_ASSERT(gs != 0); + + return gs; +} + +void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) +{ + FT_Face face = lockFace(Unscaled); + FT_Set_Transform(face, 0, 0); + FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); + + int left = face->glyph->metrics.horiBearingX; + int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width; + int top = face->glyph->metrics.horiBearingY; + int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height; + + QFixedPoint p; + p.x = 0; + p.y = 0; + + metrics->width = QFixed::fromFixed(right-left); + metrics->height = QFixed::fromFixed(top-bottom); + metrics->x = QFixed::fromFixed(left); + metrics->y = QFixed::fromFixed(-top); + metrics->xoff = QFixed::fromFixed(face->glyph->advance.x); + + if (!FT_IS_SCALABLE(freetype->face)) + QFreetypeFace::addBitmapToPath(face->glyph, p, path); + else + QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6); + + FT_Set_Transform(face, &freetype->matrix, 0); + unlockFace(); +} + +bool QFontEngineFT::supportsTransformation(const QTransform &transform) const +{ + return transform.type() <= QTransform::TxRotate; +} + +void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) +{ + if (!glyphs.numGlyphs) + return; + + if (FT_IS_SCALABLE(freetype->face)) { + QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); + } else { + QVarLengthArray positions; + QVarLengthArray positioned_glyphs; + QTransform matrix; + matrix.translate(x, y); + getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions); + + FT_Face face = lockFace(Unscaled); + for (int gl = 0; gl < glyphs.numGlyphs; gl++) { + FT_UInt glyph = positioned_glyphs[gl]; + FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO); + QFreetypeFace::addBitmapToPath(face->glyph, positions[gl], path); + } + unlockFace(); + } +} + +void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, + QPainterPath *path, QTextItem::RenderFlags) +{ + FT_Face face = lockFace(Unscaled); + + for (int gl = 0; gl < numGlyphs; gl++) { + FT_UInt glyph = glyphs[gl]; + + FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); + + FT_GlyphSlot g = face->glyph; + if (g->format != FT_GLYPH_FORMAT_OUTLINE) + continue; + if (embolden) + FT_GlyphSlot_Embolden(g); + if (obliquen) + FT_GlyphSlot_Oblique(g); + QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize); + } + unlockFace(); +} + +glyph_t QFontEngineFT::glyphIndex(uint ucs4) const +{ + glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0; + if (glyph == 0) { + FT_Face face = freetype->face; + glyph = FT_Get_Char_Index(face, ucs4); + if (glyph == 0) { + // Certain fonts don't have no-break space and tab, + // while we usually want to render them as space + if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) { + glyph = FT_Get_Char_Index(face, QChar::Space); + } else if (freetype->symbol_map) { + // Symbol fonts can have more than one CMAPs, FreeType should take the + // correct one for us by default, so we always try FT_Get_Char_Index + // first. If it didn't work (returns 0), we will explicitly set the + // CMAP to symbol font one and try again. symbol_map is not always the + // correct one because in certain fonts like Wingdings symbol_map only + // contains PUA codepoints instead of the common ones. + FT_Set_Charmap(face, freetype->symbol_map); + glyph = FT_Get_Char_Index(face, ucs4); + FT_Set_Charmap(face, freetype->unicode_map); + } + } + if (ucs4 < QFreetypeFace::cmapCacheSize) + freetype->cmapCache[ucs4] = glyph; + } + + return glyph; +} + +bool 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; + } + + int glyph_pos = 0; + if (freetype->symbol_map) { + FT_Face face = freetype->face; + QStringIterator it(str, str + len); + while (it.hasNext()) { + uint uc = it.next(); + glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; + if ( !glyphs->glyphs[glyph_pos] ) { + // Symbol fonts can have more than one CMAPs, FreeType should take the + // correct one for us by default, so we always try FT_Get_Char_Index + // first. If it didn't work (returns 0), we will explicitly set the + // CMAP to symbol font one and try again. symbol_map is not always the + // correct one because in certain fonts like Wingdings symbol_map only + // contains PUA codepoints instead of the common ones. + glyph_t glyph = FT_Get_Char_Index(face, uc); + // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9), + // while we usually want to render them as space + if (!glyph && (uc == 0xa0 || uc == 0x9)) { + uc = 0x20; + glyph = FT_Get_Char_Index(face, uc); + } + if (!glyph) { + FT_Set_Charmap(face, freetype->symbol_map); + glyph = FT_Get_Char_Index(face, uc); + FT_Set_Charmap(face, freetype->unicode_map); + } + glyphs->glyphs[glyph_pos] = glyph; + if (uc < QFreetypeFace::cmapCacheSize) + freetype->cmapCache[uc] = glyph; + } + ++glyph_pos; + } + } else { + FT_Face face = freetype->face; + QStringIterator it(str, str + len); + while (it.hasNext()) { + uint uc = it.next(); + glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; + if (!glyphs->glyphs[glyph_pos]) { + { + redo: + glyph_t glyph = FT_Get_Char_Index(face, uc); + if (!glyph && (uc == 0xa0 || uc == 0x9)) { + uc = 0x20; + goto redo; + } + glyphs->glyphs[glyph_pos] = glyph; + if (uc < QFreetypeFace::cmapCacheSize) + freetype->cmapCache[uc] = glyph; + } + } + ++glyph_pos; + } + } + + *nglyphs = glyph_pos; + glyphs->numGlyphs = glyph_pos; + + if (!(flags & GlyphIndicesOnly)) + recalcAdvances(glyphs, flags); + + return true; +} + +bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const +{ + if (!FT_IS_SCALABLE(freetype->face)) + return false; + + return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics); +} + +QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const +{ + return m * scalableBitmapScaleFactor; +} + +glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m) const +{ + glyph_metrics_t metrics; + metrics.x = scaledBitmapMetrics(m.x); + metrics.y = scaledBitmapMetrics(m.y); + metrics.width = scaledBitmapMetrics(m.width); + metrics.height = scaledBitmapMetrics(m.height); + metrics.xoff = scaledBitmapMetrics(m.xoff); + metrics.yoff = scaledBitmapMetrics(m.yoff); + return metrics; +} + +void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const +{ + FT_Face face = 0; + bool design = shouldUseDesignMetrics(flags); + for (int i = 0; i < glyphs->numGlyphs; i++) { + Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : 0; + // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph + GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono; + if (g && g->format == acceptableFormat) { + glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); + } else { + if (!face) + face = lockFace(); + g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs->glyphs[i], 0, Format_None, true); + if (g) + glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); + else + glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10) + : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round(); + if (!cacheEnabled && g != &emptyGlyph) + delete g; + } + + if (scalableBitmapScaleFactor != 1) + glyphs->advances[i] *= scalableBitmapScaleFactor; + } + if (face) + unlockFace(); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < glyphs->numGlyphs; ++i) + glyphs->advances[i] = glyphs->advances[i].round(); + } +} + +glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) +{ + FT_Face face = 0; + + glyph_metrics_t overall; + // initialize with line height, we get the same behaviour on all platforms + if (!isScalableBitmap()) { + overall.y = -ascent(); + overall.height = ascent() + descent(); + } else { + overall.y = QFixed::fromFixed(-metrics.ascender); + overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender); + } + + QFixed ymax = 0; + QFixed xmax = 0; + for (int i = 0; i < glyphs.numGlyphs; i++) { + Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : 0; + if (!g) { + if (!face) + face = lockFace(); + g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs.glyphs[i], 0, Format_None, true); + } + if (g) { + QFixed x = overall.xoff + glyphs.offsets[i].x + g->x; + 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; + if (!cacheEnabled && g != &emptyGlyph) + delete g; + } else { + int left = FLOOR(face->glyph->metrics.horiBearingX); + int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + int top = CEIL(face->glyph->metrics.horiBearingY); + int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + + QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left)); + QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top); + overall.x = qMin(overall.x, x); + 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.height = qMax(overall.height, ymax - overall.y); + overall.width = xmax - overall.x; + + if (face) + unlockFace(); + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); + return overall; +} + +glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) +{ + FT_Face face = 0; + glyph_metrics_t overall; + Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : 0; + if (!g) { + face = lockFace(); + g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, 0, Format_None, true); + } + if (g) { + overall.x = g->x; + overall.y = -g->y; + overall.width = g->width; + overall.height = g->height; + overall.xoff = g->advance; + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + overall.xoff = overall.xoff.round(); + if (!cacheEnabled && g != &emptyGlyph) + delete g; + } else { + int left = FLOOR(face->glyph->metrics.horiBearingX); + int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + int top = CEIL(face->glyph->metrics.horiBearingY); + int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + + overall.width = TRUNC(right-left); + overall.height = TRUNC(top-bottom); + overall.x = TRUNC(left); + overall.y = -TRUNC(top); + overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); + } + if (face) + unlockFace(); + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); + return overall; +} + +glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix) +{ + return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None); +} + +glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format) +{ + Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true); + + glyph_metrics_t overall; + if (g) { + overall.x = g->x; + overall.y = -g->y; + overall.width = g->width; + overall.height = g->height; + overall.xoff = g->advance; + if (!cacheEnabled && g != &emptyGlyph) + delete g; + } else { + FT_Face face = lockFace(); + int left = FLOOR(face->glyph->metrics.horiBearingX); + int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + int top = CEIL(face->glyph->metrics.horiBearingY); + int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + + overall.width = TRUNC(right-left); + overall.height = TRUNC(top-bottom); + overall.x = TRUNC(left); + overall.y = -TRUNC(top); + overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); + unlockFace(); + } + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); + return overall; +} + +static inline QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEngine::GlyphFormat glyphFormat) +{ + if (glyph == Q_NULLPTR || glyph->height == 0 || glyph->width == 0) + return QImage(); + + QImage::Format format = QImage::Format_Invalid; + int bytesPerLine = -1; + switch (glyphFormat) { + case QFontEngine::Format_Mono: + format = QImage::Format_Mono; + bytesPerLine = ((glyph->width + 31) & ~31) >> 3; + break; + case QFontEngine::Format_A8: + format = QImage::Format_Alpha8; + bytesPerLine = (glyph->width + 3) & ~3; + break; + case QFontEngine::Format_A32: + format = QImage::Format_RGB32; + bytesPerLine = glyph->width * 4; + break; + default: + Q_UNREACHABLE(); + }; + + QImage img(static_cast(glyph->data), glyph->width, glyph->height, bytesPerLine, format); + if (format == QImage::Format_Mono) + img.setColor(1, QColor(Qt::white).rgba()); // Expands color table to 2 items; item 0 set to transparent. + return img; +} + +QImage *QFontEngineFT::lockedAlphaMapForGlyph(glyph_t glyphIndex, QFixed subPixelPosition, + QFontEngine::GlyphFormat neededFormat, + const QTransform &t, QPoint *offset) +{ + Q_ASSERT(currentlyLockedAlphaMap.isNull()); + + if (isBitmapFont()) + neededFormat = Format_Mono; + else if (neededFormat == Format_None && defaultFormat != Format_None) + neededFormat = defaultFormat; + else if (neededFormat == Format_None) + neededFormat = Format_A8; + + Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t); + + if (offset != 0 && glyph != 0) + *offset = QPoint(glyph->x, -glyph->y); + + currentlyLockedAlphaMap = alphaMapFromGlyphData(glyph, neededFormat); + + const bool glyphHasGeometry = glyph != Q_NULLPTR && glyph->height != 0 && glyph->width != 0; + if (!cacheEnabled && glyph != &emptyGlyph) { + currentlyLockedAlphaMap = currentlyLockedAlphaMap.copy(); + delete glyph; + } + + if (!glyphHasGeometry) + return Q_NULLPTR; + + if (currentlyLockedAlphaMap.isNull()) + return QFontEngine::lockedAlphaMapForGlyph(glyphIndex, subPixelPosition, neededFormat, t, offset); + + QImageData *data = currentlyLockedAlphaMap.data_ptr(); + data->is_locked = true; + + return ¤tlyLockedAlphaMap; +} + +void QFontEngineFT::unlockAlphaMapForGlyph() +{ + QFontEngine::unlockAlphaMapForGlyph(); +} + +static inline bool is2dRotation(const QTransform &t) +{ + return qFuzzyCompare(t.m11(), t.m22()) && qFuzzyCompare(t.m12(), -t.m21()) + && qFuzzyCompare(t.m11()*t.m22() - t.m12()*t.m21(), qreal(1.0)); +} + +QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, + QFixed subPixelPosition, + GlyphFormat format, + const QTransform &t, + bool fetchBoundingBox, + bool disableOutlineDrawing) +{ + QGlyphSet *glyphSet = loadGlyphSet(t); + if (glyphSet != 0 && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox) + return 0; + + Glyph *glyph = glyphSet != 0 ? glyphSet->getGlyph(g, subPixelPosition) : 0; + if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) { + QScopedValueRollback saved_default_hint_style(default_hint_style); + if (t.type() >= QTransform::TxScale && !is2dRotation(t)) + default_hint_style = HintNone; // disable hinting if the glyphs are transformed + + lockFace(); + FT_Matrix m = this->matrix; + FT_Matrix ftMatrix = glyphSet != 0 ? glyphSet->transformationMatrix : QTransformToFTMatrix(t); + FT_Matrix_Multiply(&ftMatrix, &m); + freetype->matrix = m; + glyph = loadGlyph(glyphSet, g, subPixelPosition, format, false, disableOutlineDrawing); + unlockFace(); + } + + return glyph; +} + +QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition) +{ + return alphaMapForGlyph(g, subPixelPosition, QTransform()); +} + +QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) +{ + const GlyphFormat neededFormat = antialias ? Format_A8 : Format_Mono; + + Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); + + QImage img = alphaMapFromGlyphData(glyph, neededFormat); + img = img.copy(); + + if (!cacheEnabled && glyph != &emptyGlyph) + delete glyph; + + if (!img.isNull()) + return img; + + return QFontEngine::alphaMapForGlyph(g, subPixelPosition, t); +} + +QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) +{ + if (t.type() > QTransform::TxRotate) + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); + + const GlyphFormat neededFormat = Format_A32; + + Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); + + QImage img = alphaMapFromGlyphData(glyph, neededFormat); + img = img.copy(); + + if (!cacheEnabled && glyph != &emptyGlyph) + delete glyph; + + if (!img.isNull()) + return img; + + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); +} + +QImage QFontEngineFT::bitmapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) +{ + Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t); + if (glyph == Q_NULLPTR) + return QImage(); + + QImage img; + if (defaultFormat == GlyphFormat::Format_ARGB) + img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy(); + else if (defaultFormat == GlyphFormat::Format_Mono) + img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy(); + + if (!img.isNull() && (!t.isIdentity() || scalableBitmapScaleFactor != 1)) { + QTransform trans(t); + const qreal scaleFactor = scalableBitmapScaleFactor.toReal(); + trans.scale(scaleFactor, scaleFactor); + img = img.transformed(trans, Qt::SmoothTransformation); + } + + if (!cacheEnabled && glyph != &emptyGlyph) + delete glyph; + + return img; +} + +void QFontEngineFT::removeGlyphFromCache(glyph_t glyph) +{ + defaultGlyphSet.removeGlyphFromCache(glyph, 0); +} + +int QFontEngineFT::glyphCount() const +{ + int count = 0; + FT_Face face = lockFace(); + if (face) { + count = face->num_glyphs; + unlockFace(); + } + return count; +} + +FT_Face QFontEngineFT::lockFace(Scaling scale) const +{ + freetype->lock(); + FT_Face face = freetype->face; + if (scale == Unscaled) { + if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) { + freetype->xsize = face->units_per_EM << 6; + freetype->ysize = face->units_per_EM << 6; + } + } else if (freetype->xsize != xsize || freetype->ysize != ysize) { + FT_Set_Char_Size(face, xsize, ysize, 0, 0); + freetype->xsize = xsize; + freetype->ysize = ysize; + } + if (freetype->matrix.xx != matrix.xx || + freetype->matrix.yy != matrix.yy || + freetype->matrix.xy != matrix.xy || + freetype->matrix.yx != matrix.yx) { + freetype->matrix = matrix; + FT_Set_Transform(face, &freetype->matrix, 0); + } + + return face; +} + +void QFontEngineFT::unlockFace() const +{ + freetype->unlock(); +} + +FT_Face QFontEngineFT::non_locked_face() const +{ + return freetype->face; +} + + +QFontEngineFT::QGlyphSet::QGlyphSet() + : outline_drawing(false) +{ + transformationMatrix.xx = 0x10000; + transformationMatrix.yy = 0x10000; + transformationMatrix.xy = 0; + transformationMatrix.yx = 0; + memset(fast_glyph_data, 0, sizeof(fast_glyph_data)); + fast_glyph_count = 0; +} + +QFontEngineFT::QGlyphSet::~QGlyphSet() +{ + clear(); +} + +void QFontEngineFT::QGlyphSet::clear() +{ + if (fast_glyph_count > 0) { + for (int i = 0; i < 256; ++i) { + if (fast_glyph_data[i]) { + delete fast_glyph_data[i]; + fast_glyph_data[i] = 0; + } + } + fast_glyph_count = 0; + } + qDeleteAll(glyph_data); + glyph_data.clear(); +} + +void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition) +{ + if (useFastGlyphData(index, subPixelPosition)) { + if (fast_glyph_data[index]) { + delete fast_glyph_data[index]; + fast_glyph_data[index] = 0; + if (fast_glyph_count > 0) + --fast_glyph_count; + } + } else { + delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition)); + } +} + +void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph) +{ + if (useFastGlyphData(index, subPixelPosition)) { + if (!fast_glyph_data[index]) + ++fast_glyph_count; + fast_glyph_data[index] = glyph; + } else { + glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph); + } +} + +int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) +{ + lockFace(); + bool hsubpixel = true; + int vfactor = 1; + int load_flags = loadFlags(0, Format_A8, flags, hsubpixel, vfactor); + int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints); + unlockFace(); + return result; +} + +bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe) +{ + if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype)) + return false; + + // Increase the reference of this QFreetypeFace since one more QFontEngineFT + // will be using it + freetype->ref.ref(); + + default_load_flags = fe->default_load_flags; + default_hint_style = fe->default_hint_style; + antialias = fe->antialias; + transform = fe->transform; + embolden = fe->embolden; + obliquen = fe->obliquen; + subpixelType = fe->subpixelType; + lcdFilterType = fe->lcdFilterType; + embeddedbitmap = fe->embeddedbitmap; + + return true; +} + +QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const +{ + QFontDef fontDef(this->fontDef); + fontDef.pixelSize = pixelSize; + QFontEngineFT *fe = new QFontEngineFT(fontDef); + if (!fe->initFromFontEngine(this)) { + delete fe; + return 0; + } else { + return fe; + } +} + +Qt::HANDLE QFontEngineFT::handle() const +{ + return non_locked_face(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_FREETYPE diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h b/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h new file mode 100644 index 0000000000..3fa0b2e7d6 --- /dev/null +++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ +#ifndef QFONTENGINE_FT_P_H +#define QFONTENGINE_FT_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qfontengine_p.h" + +#ifndef QT_NO_FREETYPE + +#include +#include FT_FREETYPE_H + + +#ifndef Q_OS_WIN +#include +#endif + +#include + +QT_BEGIN_NAMESPACE + +class QFontEngineFTRawFont; +class QFontconfigDatabase; + +/* + * This class represents one font file on disk (like Arial.ttf) and is shared between all the font engines + * that show this font file (at different pixel sizes). + */ +class QFreetypeFace +{ +public: + void computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor); + QFontEngine::Properties properties() const; + bool getSfntTable(uint tag, uchar *buffer, uint *length) const; + + static QFreetypeFace *getFace(const QFontEngine::FaceId &face_id, + const QByteArray &fontData = QByteArray()); + void release(const QFontEngine::FaceId &face_id); + + // locks the struct for usage. Any read/write operations require locking. + void lock() + { + _lock.lock(); + } + void unlock() + { + _lock.unlock(); + } + + FT_Face face; + int xsize; // 26.6 + int ysize; // 26.6 + FT_Matrix matrix; + FT_CharMap unicode_map; + FT_CharMap symbol_map; + + enum { cmapCacheSize = 0x200 }; + glyph_t cmapCache[cmapCacheSize]; + + int fsType() const; + + int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints); + + 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); + static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path); + +private: + friend class QFontEngineFT; + friend class QtFreetypeData; + friend struct QScopedPointerDeleter; + QFreetypeFace() : _lock(QMutex::Recursive) {} + ~QFreetypeFace() {} + void cleanup(); + QAtomicInt ref; + QMutex _lock; + QByteArray fontData; + + QFontEngine::Holder hbFace; +}; + +class QFontEngineFT : public QFontEngine +{ +public: + + /* we don't cache glyphs that are too large anyway, so we can make this struct rather small */ + struct Glyph { + ~Glyph(); + short linearAdvance; + unsigned char width; + unsigned char height; + short x; + short y; + short advance; + signed char format; + uchar *data; + }; + + struct GlyphInfo { + int linearAdvance; + unsigned short width; + unsigned short height; + short x; + short y; + short xOff; + short yOff; + }; + + struct GlyphAndSubPixelPosition + { + GlyphAndSubPixelPosition(glyph_t g, QFixed spp) : glyph(g), subPixelPosition(spp) {} + + bool operator==(const GlyphAndSubPixelPosition &other) const + { + return glyph == other.glyph && subPixelPosition == other.subPixelPosition; + } + + glyph_t glyph; + QFixed subPixelPosition; + }; + + struct QGlyphSet + { + QGlyphSet(); + ~QGlyphSet(); + FT_Matrix transformationMatrix; + bool outline_drawing; + + void removeGlyphFromCache(glyph_t index, QFixed subPixelPosition); + void clear(); + inline bool useFastGlyphData(glyph_t index, QFixed subPixelPosition) const { + return (index < 256 && subPixelPosition == 0); + } + inline Glyph *getGlyph(glyph_t index, QFixed subPixelPosition = 0) const; + void setGlyph(glyph_t index, QFixed spp, Glyph *glyph); + + inline bool isGlyphMissing(glyph_t index) const { return missing_glyphs.contains(index); } + inline void setGlyphMissing(glyph_t index) const { missing_glyphs.insert(index); } +private: + mutable QHash glyph_data; // maps from glyph index to glyph data + mutable QSet missing_glyphs; + mutable Glyph *fast_glyph_data[256]; // for fast lookup of glyphs < 256 + mutable int fast_glyph_count; + }; + + QFontEngine::FaceId faceId() const Q_DECL_OVERRIDE; + QFontEngine::Properties properties() const Q_DECL_OVERRIDE; + QFixed emSquareSize() const Q_DECL_OVERRIDE; + bool supportsSubPixelPositions() const Q_DECL_OVERRIDE + { + return default_hint_style == HintLight || + default_hint_style == HintNone; + } + + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const Q_DECL_OVERRIDE; + int synthesized() const Q_DECL_OVERRIDE; + + QFixed ascent() const Q_DECL_OVERRIDE; + QFixed capHeight() const Q_DECL_OVERRIDE; + QFixed descent() const Q_DECL_OVERRIDE; + QFixed leading() const Q_DECL_OVERRIDE; + QFixed xHeight() const Q_DECL_OVERRIDE; + QFixed averageCharWidth() const Q_DECL_OVERRIDE; + + qreal maxCharWidth() const Q_DECL_OVERRIDE; + QFixed lineThickness() const Q_DECL_OVERRIDE; + QFixed underlinePosition() const Q_DECL_OVERRIDE; + + glyph_t glyphIndex(uint ucs4) const Q_DECL_OVERRIDE; + void doKerning(QGlyphLayout *, ShaperFlags) const Q_DECL_OVERRIDE; + + void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) Q_DECL_OVERRIDE; + + bool supportsTransformation(const QTransform &transform) const Q_DECL_OVERRIDE; + + void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; + void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, + QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; + + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const Q_DECL_OVERRIDE; + + glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) Q_DECL_OVERRIDE; + glyph_metrics_t boundingBox(glyph_t glyph) Q_DECL_OVERRIDE; + glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix) Q_DECL_OVERRIDE; + + void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags flags) const Q_DECL_OVERRIDE; + QImage alphaMapForGlyph(glyph_t g) Q_DECL_OVERRIDE { return alphaMapForGlyph(g, 0); } + QImage alphaMapForGlyph(glyph_t, QFixed) Q_DECL_OVERRIDE; + QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; + QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; + QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; + glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, + QFixed subPixelPosition, + const QTransform &matrix, + QFontEngine::GlyphFormat format) Q_DECL_OVERRIDE; + QImage *lockedAlphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, + GlyphFormat neededFormat, const QTransform &t, + QPoint *offset) Q_DECL_OVERRIDE; + bool hasInternalCaching() const Q_DECL_OVERRIDE { return cacheEnabled; } + void unlockAlphaMapForGlyph() Q_DECL_OVERRIDE; + bool expectsGammaCorrectedBlending() const Q_DECL_OVERRIDE; + + void removeGlyphFromCache(glyph_t glyph) Q_DECL_OVERRIDE; + int glyphMargin(QFontEngine::GlyphFormat /* format */) Q_DECL_OVERRIDE { return 0; } + + int glyphCount() const Q_DECL_OVERRIDE; + + enum Scaling { + Scaled, + Unscaled + }; + FT_Face lockFace(Scaling scale = Scaled) const; + void unlockFace() const; + + FT_Face non_locked_face() const; + + inline bool drawAntialiased() const { return antialias; } + inline bool invalid() const { return xsize == 0 && ysize == 0; } + inline bool isBitmapFont() const { return defaultFormat == Format_Mono; } + inline bool isScalableBitmap() const { return freetype->isScalableBitmap(); } + + inline Glyph *loadGlyph(uint glyph, QFixed subPixelPosition, GlyphFormat format = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const + { return loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, subPixelPosition, format, fetchMetricsOnly, disableOutlineDrawing); } + Glyph *loadGlyph(QGlyphSet *set, uint glyph, QFixed subPixelPosition, GlyphFormat = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const; + Glyph *loadGlyphFor(glyph_t g, QFixed subPixelPosition, GlyphFormat format, const QTransform &t, bool fetchBoundingBox = false, bool disableOutlineDrawing = false); + + QGlyphSet *loadGlyphSet(const QTransform &matrix); + + QFontEngineFT(const QFontDef &fd); + virtual ~QFontEngineFT(); + + bool init(FaceId faceId, bool antiaalias, GlyphFormat defaultFormat = Format_None, + const QByteArray &fontData = QByteArray()); + bool init(FaceId faceId, bool antialias, GlyphFormat format, + QFreetypeFace *freetypeFace); + + int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) Q_DECL_OVERRIDE; + + void setQtDefaultHintStyle(QFont::HintingPreference hintingPreference); + void setDefaultHintStyle(HintStyle style) Q_DECL_OVERRIDE; + + QFontEngine *cloneWithSize(qreal pixelSize) const Q_DECL_OVERRIDE; + Qt::HANDLE handle() const Q_DECL_OVERRIDE; + bool initFromFontEngine(const QFontEngineFT *fontEngine); + + HintStyle defaultHintStyle() const { return default_hint_style; } +protected: + + QFreetypeFace *freetype; + mutable int default_load_flags; + HintStyle default_hint_style; + bool antialias; + bool transform; + bool embolden; + bool obliquen; + SubpixelAntialiasingType subpixelType; + int lcdFilterType; + bool embeddedbitmap; + bool cacheEnabled; + bool forceAutoHint; + bool stemDarkeningDriver; + +private: + friend class QFontEngineFTRawFont; + friend class QFontconfigDatabase; + friend class QFreeTypeFontDatabase; + friend class QCoreTextFontDatabase; + friend class QFontEngineMultiFontConfig; + + int loadFlags(QGlyphSet *set, GlyphFormat format, int flags, bool &hsubpixel, int &vfactor) const; + bool shouldUseDesignMetrics(ShaperFlags flags) const; + QFixed scaledBitmapMetrics(QFixed m) const; + glyph_metrics_t scaledBitmapMetrics(const glyph_metrics_t &m) const; + + GlyphFormat defaultFormat; + FT_Matrix matrix; + + QList transformedGlyphSets; + mutable QGlyphSet defaultGlyphSet; + + QFontEngine::FaceId face_id; + + int xsize; + int ysize; + + QFixed line_thickness; + QFixed underline_position; + + FT_Size_Metrics metrics; + mutable bool kerning_pairs_loaded; + QFixed scalableBitmapScaleFactor; +}; + +inline uint qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g) +{ + return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt(); +} + +inline QFontEngineFT::Glyph *QFontEngineFT::QGlyphSet::getGlyph(glyph_t index, QFixed subPixelPosition) const +{ + if (useFastGlyphData(index, subPixelPosition)) + return fast_glyph_data[index]; + return glyph_data.value(GlyphAndSubPixelPosition(index, subPixelPosition)); +} + +extern FT_Library qt_getFreetype(); + +QT_END_NAMESPACE + +#endif // QT_NO_FREETYPE + +#endif // QFONTENGINE_FT_P_H diff --git a/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp b/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp new file mode 100644 index 0000000000..97be1606b4 --- /dev/null +++ b/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "qfreetypefontdatabase_p.h" + +#include +#include + +#include +#include +#include +#include +#include + +#undef QT_NO_FREETYPE +#include + +#include +#include FT_TRUETYPE_TABLES_H +#include FT_ERRORS_H + +QT_BEGIN_NAMESPACE + +void QFreeTypeFontDatabase::populateFontDatabase() +{ + QString fontpath = fontDir(); + QDir dir(fontpath); + + if (!dir.exists()) { + qWarning("QFontDatabase: Cannot find font directory %s.\n" + "Note that Qt no longer ships fonts. Deploy some (from http://dejavu-fonts.org for example) or switch to fontconfig.", + qPrintable(fontpath)); + return; + } + + QStringList nameFilters; + nameFilters << QLatin1String("*.ttf") + << QLatin1String("*.ttc") + << QLatin1String("*.pfa") + << QLatin1String("*.pfb") + << QLatin1String("*.otf"); + + const auto fis = dir.entryInfoList(nameFilters, QDir::Files); + for (const QFileInfo &fi : fis) { + const QByteArray file = QFile::encodeName(fi.absoluteFilePath()); + QFreeTypeFontDatabase::addTTFile(QByteArray(), file); + } +} + +QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *usrPtr) +{ + FontFile *fontfile = static_cast (usrPtr); + QFontEngine::FaceId fid; + fid.filename = QFile::encodeName(fontfile->fileName); + fid.index = fontfile->indexValue; + + bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); + QFontEngineFT *engine = new QFontEngineFT(fontDef); + QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono; + if (antialias) { + QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint(); + if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) { + format = QFontEngineFT::Format_A8; + engine->subpixelType = QFontEngine::Subpixel_None; + } else { + format = QFontEngineFT::Format_A32; + engine->subpixelType = subpixelType; + } + } + + if (!engine->init(fid, antialias, format) || engine->invalid()) { + delete engine; + engine = 0; + } else { + engine->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); + } + + return engine; +} + +namespace { + + class QFontEngineFTRawData: public QFontEngineFT + { + public: + QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef) + { + } + + void updateFamilyNameAndStyle() + { + fontDef.family = QString::fromLatin1(freetype->face->family_name); + + if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC) + fontDef.style = QFont::StyleItalic; + + if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD) + fontDef.weight = QFont::Bold; + } + + bool initFromData(const QByteArray &fontData) + { + FaceId faceId; + faceId.filename = ""; + faceId.index = 0; + faceId.uuid = QUuid::createUuid().toByteArray(); + + return init(faceId, true, Format_None, fontData); + } + }; + +} + +QFontEngine *QFreeTypeFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, + QFont::HintingPreference hintingPreference) +{ + QFontDef fontDef; + fontDef.pixelSize = pixelSize; + fontDef.hintingPreference = hintingPreference; + + QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef); + if (!fe->initFromData(fontData)) { + delete fe; + return 0; + } + + fe->updateFamilyNameAndStyle(); + fe->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); + + return fe; +} + +QStringList QFreeTypeFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) +{ + return QFreeTypeFontDatabase::addTTFile(fontData, fileName.toLocal8Bit()); +} + +void QFreeTypeFontDatabase::releaseHandle(void *handle) +{ + FontFile *file = static_cast(handle); + delete file; +} + +extern FT_Library qt_getFreetype(); + +QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file) +{ + FT_Library library = qt_getFreetype(); + + int index = 0; + int numFaces = 0; + QStringList families; + do { + FT_Face face; + FT_Error error; + if (!fontData.isEmpty()) { + error = FT_New_Memory_Face(library, (const FT_Byte *)fontData.constData(), fontData.size(), index, &face); + } else { + error = FT_New_Face(library, file.constData(), index, &face); + } + if (error != FT_Err_Ok) { + qDebug() << "FT_New_Face failed with index" << index << ':' << hex << error; + break; + } + numFaces = face->num_faces; + + QFont::Weight weight = QFont::Normal; + + QFont::Style style = QFont::StyleNormal; + if (face->style_flags & FT_STYLE_FLAG_ITALIC) + style = QFont::StyleItalic; + + if (face->style_flags & FT_STYLE_FLAG_BOLD) + weight = QFont::Bold; + + bool fixedPitch = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH); + + QSupportedWritingSystems writingSystems; + // detect symbol fonts + for (int i = 0; i < face->num_charmaps; ++i) { + FT_CharMap cm = face->charmaps[i]; + if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM + || cm->encoding == FT_ENCODING_MS_SYMBOL) { + writingSystems.setSupported(QFontDatabase::Symbol); + break; + } + } + + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); + if (os2) { + quint32 unicodeRange[4] = { + quint32(os2->ulUnicodeRange1), + quint32(os2->ulUnicodeRange2), + quint32(os2->ulUnicodeRange3), + quint32(os2->ulUnicodeRange4) + }; + quint32 codePageRange[2] = { + quint32(os2->ulCodePageRange1), + quint32(os2->ulCodePageRange2) + }; + + writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + + if (os2->usWeightClass) { + weight = QPlatformFontDatabase::weightFromInteger(os2->usWeightClass); + } else if (os2->panose[2]) { + int w = os2->panose[2]; + if (w <= 1) + weight = QFont::Thin; + else if (w <= 2) + weight = QFont::ExtraLight; + else if (w <= 3) + weight = QFont::Light; + else if (w <= 5) + weight = QFont::Normal; + else if (w <= 6) + weight = QFont::Medium; + else if (w <= 7) + weight = QFont::DemiBold; + else if (w <= 8) + weight = QFont::Bold; + else if (w <= 9) + weight = QFont::ExtraBold; + else if (w <= 10) + weight = QFont::Black; + } + } + + QString family = QString::fromLatin1(face->family_name); + FontFile *fontFile = new FontFile; + fontFile->fileName = QFile::decodeName(file); + fontFile->indexValue = index; + + QFont::Stretch stretch = QFont::Unstretched; + + registerFont(family,QString::fromLatin1(face->style_name),QString(),weight,style,stretch,true,true,0,fixedPitch,writingSystems,fontFile); + + families.append(family); + + FT_Done_Face(face); + ++index; + } while (index < numFaces); + return families; +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase_p.h b/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase_p.h new file mode 100644 index 0000000000..6d51361400 --- /dev/null +++ b/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QFREETYPEFONTDATABASE_H +#define QFREETYPEFONTDATABASE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct FontFile +{ + QString fileName; + int indexValue; +}; + +class QFreeTypeFontDatabase : public QPlatformFontDatabase +{ +public: + void populateFontDatabase() Q_DECL_OVERRIDE; + QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE; + QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) Q_DECL_OVERRIDE; + QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName) Q_DECL_OVERRIDE; + void releaseHandle(void *handle) Q_DECL_OVERRIDE; + + static QStringList addTTFile(const QByteArray &fontData, const QByteArray &file); +}; + +QT_END_NAMESPACE + +#endif // QFREETYPEFONTDATABASE_H diff --git a/src/platformsupport/fontdatabases/genericunix/qgenericunixfontdatabase_p.h b/src/platformsupport/fontdatabases/genericunix/qgenericunixfontdatabase_p.h index 37c667eeb3..ccf5ad6d13 100644 --- a/src/platformsupport/fontdatabases/genericunix/qgenericunixfontdatabase_p.h +++ b/src/platformsupport/fontdatabases/genericunix/qgenericunixfontdatabase_p.h @@ -57,8 +57,8 @@ #include typedef QFontconfigDatabase QGenericUnixFontDatabase; #else -#include -typedef QBasicFontDatabase QGenericUnixFontDatabase; +#include +typedef QFreeTypeFontDatabase QGenericUnixFontDatabase; #endif //Q_FONTCONFIGDATABASE #endif // QGENERICUNIXFONTDATABASE_H diff --git a/src/platformsupport/fontdatabases/mac/coretext.pri b/src/platformsupport/fontdatabases/mac/coretext.pri index f73e22eb1a..a533234c26 100644 --- a/src/platformsupport/fontdatabases/mac/coretext.pri +++ b/src/platformsupport/fontdatabases/mac/coretext.pri @@ -3,8 +3,8 @@ OBJECTIVE_SOURCES += $$PWD/qfontengine_coretext.mm $$PWD/qcoretextfontdatabase.m qtConfig(freetype) { QMAKE_USE_PRIVATE += freetype - HEADERS += basic/qfontengine_ft_p.h - SOURCES += basic/qfontengine_ft.cpp + HEADERS += freetype/qfontengine_ft_p.h + SOURCES += freetype/qfontengine_ft.cpp } uikit: \ diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp index 65947ab7da..3f03b30f10 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp @@ -393,14 +393,14 @@ void QWindowsFontDatabaseFT::populateFontDatabase() QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, void *handle) { - QFontEngine *fe = QBasicFontDatabase::fontEngine(fontDef, handle); + QFontEngine *fe = QFreeTypeFontDatabase::fontEngine(fontDef, handle); qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef.family << fe << handle; return fe; } QFontEngine *QWindowsFontDatabaseFT::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) { - QFontEngine *fe = QBasicFontDatabase::fontEngine(fontData, pixelSize, hintingPreference); + QFontEngine *fe = QFreeTypeFontDatabase::fontEngine(fontData, pixelSize, hintingPreference); qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fe; return fe; } @@ -410,7 +410,7 @@ QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family, QF QStringList result; result.append(QWindowsFontDatabase::familyForStyleHint(styleHint)); result.append(QWindowsFontDatabase::extraTryFontsForFamily(family)); - result.append(QBasicFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + result.append(QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script)); qCDebug(lcQpaFonts) << __FUNCTION__ << family << style << styleHint << script << result; diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft_p.h b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft_p.h index 3a432842e5..2df81274ad 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft_p.h +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft_p.h @@ -51,13 +51,13 @@ // We mean it. // -#include +#include #include #include QT_BEGIN_NAMESPACE -class QWindowsFontDatabaseFT : public QBasicFontDatabase +class QWindowsFontDatabaseFT : public QFreeTypeFontDatabase { public: void populateFontDatabase() Q_DECL_OVERRIDE; diff --git a/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp index eb5a38855e..6ac092aa0e 100644 --- a/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp @@ -144,7 +144,7 @@ QWinRTFontDatabase::~QWinRTFontDatabase() QString QWinRTFontDatabase::fontDir() const { qCDebug(lcQpaFonts) << __FUNCTION__; - QString fontDirectory = QBasicFontDatabase::fontDir(); + QString fontDirectory = QFreeTypeFontDatabase::fontDir(); if (!QFile::exists(fontDirectory)) { // Fall back to app directory + fonts, and just app directory after that const QString applicationDirPath = QCoreApplication::applicationDirPath(); @@ -176,7 +176,7 @@ void QWinRTFontDatabase::populateFontDatabase() HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory1), &factory); if (FAILED(hr)) { qWarning("Failed to create DirectWrite factory: %s", qPrintable(qt_error_string(hr))); - QBasicFontDatabase::populateFontDatabase(); + QFreeTypeFontDatabase::populateFontDatabase(); return; } @@ -184,7 +184,7 @@ void QWinRTFontDatabase::populateFontDatabase() hr = factory->GetSystemFontCollection(&fontCollection); if (FAILED(hr)) { qWarning("Failed to open system font collection: %s", qPrintable(qt_error_string(hr))); - QBasicFontDatabase::populateFontDatabase(); + QFreeTypeFontDatabase::populateFontDatabase(); return; } @@ -222,7 +222,7 @@ void QWinRTFontDatabase::populateFontDatabase() registerFontFamily(familyName); } - QBasicFontDatabase::populateFontDatabase(); + QFreeTypeFontDatabase::populateFontDatabase(); } void QWinRTFontDatabase::populateFamily(const QString &familyName) @@ -399,7 +399,7 @@ QFontEngine *QWinRTFontDatabase::fontEngine(const QFontDef &fontDef, void *handl IDWriteFontFile *fontFile = reinterpret_cast(handle); if (!m_fonts.contains(fontFile)) - return QBasicFontDatabase::fontEngine(fontDef, handle); + return QFreeTypeFontDatabase::fontEngine(fontDef, handle); const void *referenceKey; quint32 referenceKeySize; @@ -468,7 +468,7 @@ QStringList QWinRTFontDatabase::fallbacksForFamily(const QString &family, QFont: QStringList result; if (family == QLatin1String("Helvetica")) result.append(QStringLiteral("Arial")); - result.append(QBasicFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + result.append(QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script)); return result; } @@ -486,7 +486,7 @@ void QWinRTFontDatabase::releaseHandle(void *handle) return; } - QBasicFontDatabase::releaseHandle(handle); + QFreeTypeFontDatabase::releaseHandle(handle); } QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase_p.h b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase_p.h index 3b803d7613..9a2bf00fab 100644 --- a/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase_p.h +++ b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase_p.h @@ -51,7 +51,7 @@ // We mean it. // -#include +#include #include struct IDWriteFontFile; @@ -67,7 +67,7 @@ struct FontDescription QByteArray uuid; }; -class QWinRTFontDatabase : public QBasicFontDatabase +class QWinRTFontDatabase : public QFreeTypeFontDatabase { public: ~QWinRTFontDatabase(); -- cgit v1.2.3 From 0d12ebd7e7e4de2c60faa443c9e711bba281de88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 24 Mar 2017 11:56:57 +0100 Subject: Share FreeType font engine creation code between platforms The FreeType font engine setup is spread out between factory functions, constructors, and init() functions, so let's at least try to share as much as possible of it to make it easier to reason about and possibly refactor in the future. Change-Id: Ic39353d2b3111024e0589a70211bac80feb8498e Reviewed-by: Eskil Abrahamsen Blomfeldt --- .../fontconfig/qfontconfigdatabase.cpp | 1 + .../fontdatabases/freetype/qfontengine_ft.cpp | 40 ++++++++++++++++++++++ .../fontdatabases/freetype/qfontengine_ft_p.h | 4 ++- .../freetype/qfreetypefontdatabase.cpp | 31 +++-------------- .../fontdatabases/mac/qcoretextfontdatabase.mm | 23 +------------ .../fontdatabases/winrt/qwinrtfontdatabase.cpp | 9 +---- 6 files changed, 51 insertions(+), 57 deletions(-) (limited to 'src/platformsupport/fontdatabases') diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 4464e93bbd..a997bf0ba1 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -677,6 +677,7 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr) fid.filename = QFile::encodeName(fontfile->fileName); fid.index = fontfile->indexValue; + // FIXME: Unify with logic in QFontEngineFT::create() QFontEngineFT *engine = new QFontEngineFT(f); engine->face_id = fid; diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp index 03e72546eb..98bf91fa9d 100644 --- a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp +++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp @@ -44,6 +44,9 @@ #include "qfontengine_ft_p.h" #include "private/qimage_p.h" #include +#include +#include +#include #ifndef QT_NO_FREETYPE @@ -666,6 +669,43 @@ static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, } } +static QFontEngine::SubpixelAntialiasingType subpixelAntialiasingTypeHint() +{ + static int type = -1; + if (type == -1) { + if (QScreen *screen = QGuiApplication::primaryScreen()) + type = screen->handle()->subpixelAntialiasingTypeHint(); + } + return static_cast(type); +} + +QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData) +{ + QScopedPointer engine(new QFontEngineFT(fontDef)); + + QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono; + const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); + + if (antialias) { + QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint(); + if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) { + format = QFontEngineFT::Format_A8; + engine->subpixelType = QFontEngine::Subpixel_None; + } else { + format = QFontEngineFT::Format_A32; + engine->subpixelType = subpixelType; + } + } + + if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) { + qWarning("QFontEngineFT: Failed to create FreeType font engine"); + return nullptr; + } + + engine->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); + return engine.take(); +} + QFontEngineFT::QFontEngineFT(const QFontDef &fd) : QFontEngine(Freetype) { diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h b/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h index 3fa0b2e7d6..fc923d196b 100644 --- a/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h +++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h @@ -292,6 +292,9 @@ private: bool initFromFontEngine(const QFontEngineFT *fontEngine); HintStyle defaultHintStyle() const { return default_hint_style; } + + static QFontEngineFT *create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData = QByteArray()); + protected: QFreetypeFace *freetype; @@ -312,7 +315,6 @@ private: friend class QFontEngineFTRawFont; friend class QFontconfigDatabase; friend class QFreeTypeFontDatabase; - friend class QCoreTextFontDatabase; friend class QFontEngineMultiFontConfig; int loadFlags(QGlyphSet *set, GlyphFormat format, int flags, bool &hsubpixel, int &vfactor) const; diff --git a/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp b/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp index 97be1606b4..764f2d8868 100644 --- a/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp +++ b/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp @@ -85,33 +85,12 @@ void QFreeTypeFontDatabase::populateFontDatabase() QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *usrPtr) { - FontFile *fontfile = static_cast (usrPtr); - QFontEngine::FaceId fid; - fid.filename = QFile::encodeName(fontfile->fileName); - fid.index = fontfile->indexValue; - - bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); - QFontEngineFT *engine = new QFontEngineFT(fontDef); - QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono; - if (antialias) { - QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint(); - if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) { - format = QFontEngineFT::Format_A8; - engine->subpixelType = QFontEngine::Subpixel_None; - } else { - format = QFontEngineFT::Format_A32; - engine->subpixelType = subpixelType; - } - } - - if (!engine->init(fid, antialias, format) || engine->invalid()) { - delete engine; - engine = 0; - } else { - engine->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); - } + FontFile *fontfile = static_cast(usrPtr); + QFontEngine::FaceId faceId; + faceId.filename = QFile::encodeName(fontfile->fileName); + faceId.index = fontfile->indexValue; - return engine; + return QFontEngineFT::create(fontDef, faceId); } namespace { diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index 3588948133..b8097702ed 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -890,28 +890,7 @@ QFontEngine *QCoreTextFontDatabase::freeTypeFontEngine(const QFontDef &fontDef, { QFontEngine::FaceId faceId; faceId.filename = filename; - const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); - - QScopedPointer engine(new QFontEngineFT(fontDef)); - QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono; - if (antialias) { - QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint(); - if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) { - format = QFontEngineFT::Format_A8; - engine->subpixelType = QFontEngine::Subpixel_None; - } else { - format = QFontEngineFT::Format_A32; - engine->subpixelType = subpixelType; - } - } - - if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) { - qWarning("QCoreTextFontDatabase::freeTypefontEngine Failed to create engine"); - return Q_NULLPTR; - } - engine->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); - - return engine.take(); + return QFontEngineFT::create(fontDef, faceId, fontData); } #endif diff --git a/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp index 6ac092aa0e..2a95ca26a9 100644 --- a/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp @@ -444,15 +444,8 @@ QFontEngine *QWinRTFontDatabase::fontEngine(const QFontDef &fontDef, void *handl const FontDescription description = m_fonts.value(fontFile); faceId.uuid = description.uuid; faceId.index = description.index; - const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); - QFontEngineFT::GlyphFormat format = antialias ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono; - QFontEngineFT *engine = new QFontEngineFT(fontDef); - if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) { - delete engine; - return 0; - } - return engine; + return QFontEngineFT::create(fontDef, faceId, fontData); } QStringList QWinRTFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, -- cgit v1.2.3 From 2b9ecfe69c01085fbafb6c3c83b7ca4a5f564f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 30 Mar 2017 14:35:26 +0200 Subject: macOS: Split out font engine creation from QCoreTextFontDatabase The Core Text font database can produce both Core Text and FreeType font engines. Refactor the code a bit so that the actual factory methods that differ between the two stand out, and do not require a granular runtime check in each method. Change-Id: Ib70f76f4a9001a8108d87c1101a50699a6ea8f55 Reviewed-by: Simon Hausmann --- .../fontdatabases/mac/qcoretextfontdatabase.mm | 129 +++++++++++---------- .../fontdatabases/mac/qcoretextfontdatabase_p.h | 24 ++-- 2 files changed, 84 insertions(+), 69 deletions(-) (limited to 'src/platformsupport/fontdatabases') diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index b8097702ed..fcc94c40b3 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -112,12 +112,8 @@ static NSInteger languageMapSort(id obj1, id obj2, void *context) } #endif -QCoreTextFontDatabase::QCoreTextFontDatabase(bool useFreeType) -#ifndef QT_NO_FREETYPE - : m_useFreeType(useFreeType) -#endif +QCoreTextFontDatabase::QCoreTextFontDatabase() { - Q_UNUSED(useFreeType) #ifdef Q_OS_MACX QSettings appleSettings(QLatin1String("apple.com")); QVariant appleValue = appleSettings.value(QLatin1String("AppleAntiAliasingThreshold")); @@ -393,35 +389,24 @@ static QByteArray filenameForCFUrl(CFURLRef url) extern CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef); -QFontEngine *QCoreTextFontDatabase::fontEngine(const QFontDef &f, void *usrPtr) +template <> +QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const QFontDef &fontDef, void *usrPtr) { CTFontDescriptorRef descriptor = static_cast(usrPtr); -#ifndef QT_NO_FREETYPE - if (m_useFreeType) { - QCFType url(static_cast(CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute))); - - QByteArray filename; - if (url) - filename = filenameForCFUrl(url); - - return freeTypeFontEngine(f, filename); - } -#endif - // Since we do not pass in the destination DPI to CoreText when making // the font, we need to pass in a point size which is scaled to include // the DPI. The default DPI for the screen is 72, thus the scale factor // is destinationDpi / 72, but since pixelSize = pointSize / 72 * dpi, // the pixelSize is actually the scaled point size for the destination // DPI, and we can use that directly. - qreal scaledPointSize = f.pixelSize; + qreal scaledPointSize = fontDef.pixelSize; - CGAffineTransform matrix = qt_transform_from_fontdef(f); + CGAffineTransform matrix = qt_transform_from_fontdef(fontDef); CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix); if (font) { - QFontEngine *engine = new QCoreTextFontEngine(font, f); - engine->fontDef = f; + QFontEngine *engine = new QCoreTextFontEngine(font, fontDef); + engine->fontDef = fontDef; CFRelease(font); return engine; } @@ -429,6 +414,24 @@ QFontEngine *QCoreTextFontDatabase::fontEngine(const QFontDef &f, void *usrPtr) return NULL; } +#ifndef QT_NO_FREETYPE +template <> +QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const QFontDef &fontDef, void *usrPtr) +{ + CTFontDescriptorRef descriptor = static_cast(usrPtr); + + QCFType url(static_cast(CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute))); + + QByteArray filename; + if (url) + filename = filenameForCFUrl(url); + + QFontEngine::FaceId faceId; + faceId.filename = filename; + return QFontEngineFT::create(fontDef, faceId); +} +#endif + static void releaseFontData(void* info, const void* data, size_t size) { Q_UNUSED(data); @@ -436,31 +439,9 @@ static void releaseFontData(void* info, const void* data, size_t size) delete (QByteArray*)info; } -QFontEngine *QCoreTextFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +template <> +QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) { -#ifndef QT_NO_FREETYPE - if (m_useFreeType) { - QByteArray *fontDataCopy = new QByteArray(fontData); - QCFType dataProvider = CGDataProviderCreateWithData(fontDataCopy, - fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); - QCFType cgFont(CGFontCreateWithDataProvider(dataProvider)); - - if (!cgFont) { - qWarning("QCoreTextFontDatabase::fontEngine: CGFontCreateWithDataProvider failed"); - return Q_NULLPTR; - } - - QFontDef fontDef; - fontDef.pixelSize = pixelSize; - fontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi(); - fontDef.hintingPreference = hintingPreference; - CGAffineTransform transform = qt_transform_from_fontdef(fontDef); - QCFType ctFont(CTFontCreateWithGraphicsFont(cgFont, fontDef.pixelSize, &transform, Q_NULLPTR)); - QCFType url(static_cast(CTFontCopyAttribute(ctFont, kCTFontURLAttribute))); - return freeTypeFontEngine(fontDef, filenameForCFUrl(url), fontData); - } -#endif - Q_UNUSED(hintingPreference); QByteArray* fontDataCopy = new QByteArray(fontData); @@ -483,6 +464,34 @@ QFontEngine *QCoreTextFontDatabase::fontEngine(const QByteArray &fontData, qreal return fontEngine; } +#ifndef QT_NO_FREETYPE +template <> +QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +{ + QByteArray *fontDataCopy = new QByteArray(fontData); + QCFType dataProvider = CGDataProviderCreateWithData(fontDataCopy, + fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); + QCFType cgFont(CGFontCreateWithDataProvider(dataProvider)); + + if (!cgFont) { + qWarning("QCoreTextFontDatabase::fontEngine: CGFontCreateWithDataProvider failed"); + return Q_NULLPTR; + } + + QFontDef fontDef; + fontDef.pixelSize = pixelSize; + fontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi(); + fontDef.hintingPreference = hintingPreference; + CGAffineTransform transform = qt_transform_from_fontdef(fontDef); + QCFType ctFont(CTFontCreateWithGraphicsFont(cgFont, fontDef.pixelSize, &transform, Q_NULLPTR)); + QCFType url(static_cast(CTFontCopyAttribute(ctFont, kCTFontURLAttribute))); + + QFontEngine::FaceId faceId; + faceId.filename = filenameForCFUrl(url); + return QFontEngineFT::create(fontDef, faceId, fontData); +} +#endif + QFont::StyleHint styleHintFromNSString(NSString *style) { if ([style isEqual: @"sans-serif"]) @@ -615,17 +624,27 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo return fallbackLists[styleLookupKey.arg(styleHint)]; } -CFArrayRef QCoreTextFontDatabase::createDescriptorArrayForFont(CTFontRef font, const QString &fileName) +template <> +CFArrayRef QCoreTextFontDatabaseEngineFactory::createDescriptorArrayForFont(CTFontRef font, const QString &fileName) { + Q_UNUSED(fileName) CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); QCFType descriptor = CTFontCopyFontDescriptor(font); + CFArrayAppendValue(array, descriptor); + return array; +} - Q_UNUSED(fileName) #ifndef QT_NO_FREETYPE +template <> +CFArrayRef QCoreTextFontDatabaseEngineFactory::createDescriptorArrayForFont(CTFontRef font, const QString &fileName) +{ + CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + QCFType descriptor = CTFontCopyFontDescriptor(font); + // The physical font source URL (usually a local file or Qt resource) is only required for // FreeType, when using non-system fonts, and needs some hackery to attach in a format // agreeable to OSX. - if (m_useFreeType && !fileName.isEmpty()) { + if (!fileName.isEmpty()) { QCFType fontURL; if (fileName.startsWith(QLatin1String(":/"))) { @@ -642,11 +661,11 @@ CFArrayRef QCoreTextFontDatabase::createDescriptorArrayForFont(CTFontRef font, c CFDictionaryAddValue(attributes, kCTFontURLAttribute, fontURL); descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, attributes); } -#endif CFArrayAppendValue(array, descriptor); return array; } +#endif QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) { @@ -884,15 +903,5 @@ void QCoreTextFontDatabase::removeApplicationFonts() m_applicationFonts.clear(); } -#ifndef QT_NO_FREETYPE -QFontEngine *QCoreTextFontDatabase::freeTypeFontEngine(const QFontDef &fontDef, const QByteArray &filename, - const QByteArray &fontData) -{ - QFontEngine::FaceId faceId; - faceId.filename = filename; - return QFontEngineFT::create(fontDef, faceId, fontData); -} -#endif - QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h index c093d903af..8edf60b5fa 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h @@ -72,13 +72,11 @@ QT_BEGIN_NAMESPACE class QCoreTextFontDatabase : public QPlatformFontDatabase { public: - QCoreTextFontDatabase(bool useFreeType = false); + QCoreTextFontDatabase(); ~QCoreTextFontDatabase(); void populateFontDatabase() Q_DECL_OVERRIDE; void populateFamily(const QString &familyName) Q_DECL_OVERRIDE; - QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE; - QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) Q_DECL_OVERRIDE; QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const Q_DECL_OVERRIDE; QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName) Q_DECL_OVERRIDE; void releaseHandle(void *handle) Q_DECL_OVERRIDE; @@ -93,13 +91,8 @@ public: private: void populateFromDescriptor(CTFontDescriptorRef font, const QString &familyName = QString()); - CFArrayRef createDescriptorArrayForFont(CTFontRef font, const QString &fileName); + virtual CFArrayRef createDescriptorArrayForFont(CTFontRef font, const QString &fileName) = 0; -#ifndef QT_NO_FREETYPE - bool m_useFreeType; - QFontEngine *freeTypeFontEngine(const QFontDef &fontDef, const QByteArray &filename, - const QByteArray &fontData = QByteArray()); -#endif mutable QString defaultFontName; void removeApplicationFonts(); @@ -109,6 +102,19 @@ private: mutable QHash m_themeFonts; }; +// Split out into separate template class so that the compiler doesn't have +// to generate code for each override in QCoreTextFontDatabase for each T. + +template +class QCoreTextFontDatabaseEngineFactory : public QCoreTextFontDatabase +{ +public: + QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override; + QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override; +protected: + CFArrayRef createDescriptorArrayForFont(CTFontRef font, const QString &fileName) override; +}; + QT_END_NAMESPACE #endif // QCORETEXTFONTDATABASE_H -- cgit v1.2.3 From 6c547142d5b80619ca751edade8429f6fddad853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 30 Mar 2017 15:32:12 +0200 Subject: Move FreeType font engine creation for QRawFont into factory function So that the logic can be used without subclassing the FreeType font database. Change-Id: Ib1ac0e1948ce18003b8eda0c1e8fbb3c7e6c8360 Reviewed-by: Simon Hausmann --- .../fontdatabases/freetype/qfontengine_ft.cpp | 50 ++++++++++++++++++++++ .../fontdatabases/freetype/qfontengine_ft_p.h | 1 + .../freetype/qfreetypefontdatabase.cpp | 49 +-------------------- 3 files changed, 52 insertions(+), 48 deletions(-) (limited to 'src/platformsupport/fontdatabases') diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp index 98bf91fa9d..3895681507 100644 --- a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp +++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #ifndef QT_NO_FREETYPE @@ -706,6 +707,55 @@ QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, con return engine.take(); } +namespace { + class QFontEngineFTRawData: public QFontEngineFT + { + public: + QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef) + { + } + + void updateFamilyNameAndStyle() + { + fontDef.family = QString::fromLatin1(freetype->face->family_name); + + if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC) + fontDef.style = QFont::StyleItalic; + + if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD) + fontDef.weight = QFont::Bold; + } + + bool initFromData(const QByteArray &fontData) + { + FaceId faceId; + faceId.filename = ""; + faceId.index = 0; + faceId.uuid = QUuid::createUuid().toByteArray(); + + return init(faceId, true, Format_None, fontData); + } + }; +} + +QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +{ + QFontDef fontDef; + fontDef.pixelSize = pixelSize; + fontDef.hintingPreference = hintingPreference; + + QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef); + if (!fe->initFromData(fontData)) { + delete fe; + return 0; + } + + fe->updateFamilyNameAndStyle(); + fe->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); + + return fe; +} + QFontEngineFT::QFontEngineFT(const QFontDef &fd) : QFontEngine(Freetype) { diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h b/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h index fc923d196b..2993e3b616 100644 --- a/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h +++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft_p.h @@ -294,6 +294,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); protected: diff --git a/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp b/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp index 764f2d8868..2caa47658a 100644 --- a/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp +++ b/src/platformsupport/fontdatabases/freetype/qfreetypefontdatabase.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include #undef QT_NO_FREETYPE @@ -93,56 +92,10 @@ QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *us return QFontEngineFT::create(fontDef, faceId); } -namespace { - - class QFontEngineFTRawData: public QFontEngineFT - { - public: - QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef) - { - } - - void updateFamilyNameAndStyle() - { - fontDef.family = QString::fromLatin1(freetype->face->family_name); - - if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC) - fontDef.style = QFont::StyleItalic; - - if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD) - fontDef.weight = QFont::Bold; - } - - bool initFromData(const QByteArray &fontData) - { - FaceId faceId; - faceId.filename = ""; - faceId.index = 0; - faceId.uuid = QUuid::createUuid().toByteArray(); - - return init(faceId, true, Format_None, fontData); - } - }; - -} - QFontEngine *QFreeTypeFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) { - QFontDef fontDef; - fontDef.pixelSize = pixelSize; - fontDef.hintingPreference = hintingPreference; - - QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef); - if (!fe->initFromData(fontData)) { - delete fe; - return 0; - } - - fe->updateFamilyNameAndStyle(); - fe->setQtDefaultHintStyle(static_cast(fontDef.hintingPreference)); - - return fe; + return QFontEngineFT::create(fontData, pixelSize, hintingPreference); } QStringList QFreeTypeFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) -- cgit v1.2.3 From a7fe6ab8915d0a52c130a46817a16f89717eee9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 30 Mar 2017 16:25:41 +0200 Subject: macOS: Use shared code path for creating FreeType font engine for QRawFont The round trip via a CGFontRef to a CTFontRef so that we could pull out the kCTFontURLAttribute of the font makes no sense, as the input is just raw font data. None of the tst_QRawFont tests are able to pull out a URL, and none of the other platforms where FreeType is available resolve a filename from raw fonts. Without this change the tst_QRawFont test has 13 failed test cases and spits out 1250 QWARNs. With the patch all tests pass without warnings. Change-Id: I17965e7405d7161958ff0870825ce8190598288f Reviewed-by: Simon Hausmann --- .../fontdatabases/mac/qcoretextfontdatabase.mm | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) (limited to 'src/platformsupport/fontdatabases') diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index fcc94c40b3..d40636e7d7 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -468,27 +468,7 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine template <> QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) { - QByteArray *fontDataCopy = new QByteArray(fontData); - QCFType dataProvider = CGDataProviderCreateWithData(fontDataCopy, - fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); - QCFType cgFont(CGFontCreateWithDataProvider(dataProvider)); - - if (!cgFont) { - qWarning("QCoreTextFontDatabase::fontEngine: CGFontCreateWithDataProvider failed"); - return Q_NULLPTR; - } - - QFontDef fontDef; - fontDef.pixelSize = pixelSize; - fontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi(); - fontDef.hintingPreference = hintingPreference; - CGAffineTransform transform = qt_transform_from_fontdef(fontDef); - QCFType ctFont(CTFontCreateWithGraphicsFont(cgFont, fontDef.pixelSize, &transform, Q_NULLPTR)); - QCFType url(static_cast(CTFontCopyAttribute(ctFont, kCTFontURLAttribute))); - - QFontEngine::FaceId faceId; - faceId.filename = filenameForCFUrl(url); - return QFontEngineFT::create(fontDef, faceId, fontData); + return QFontEngineFT::create(fontData, pixelSize, hintingPreference); } #endif -- cgit v1.2.3 From 5b41b34958792931b910859fd8ad812d8959d60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 30 Mar 2017 18:48:39 +0200 Subject: macOS: Use NSURL to resolve FreeType font names instead of CoreFoundation Change-Id: I4b397361c483fa07e4e95288f66d7f7ea2df8d9f Reviewed-by: Simon Hausmann --- .../fontdatabases/mac/qcoretextfontdatabase.mm | 37 ++++++---------------- 1 file changed, 9 insertions(+), 28 deletions(-) (limited to 'src/platformsupport/fontdatabases') diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index d40636e7d7..ea0aab17a0 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -363,30 +363,6 @@ void QCoreTextFontDatabase::releaseHandle(void *handle) CFRelease(CTFontDescriptorRef(handle)); } -#ifndef QT_NO_FREETYPE -static QByteArray filenameForCFUrl(CFURLRef url) -{ - // The on-stack buffer prevents that a QByteArray allocated for the worst case (MAXPATHLEN) - // stays around for the lifetime of the font. Additionally, it helps to move the char - // signedness cast to an acceptable place. - uchar buffer[MAXPATHLEN]; - QByteArray filename; - - if (!CFURLGetFileSystemRepresentation(url, true, buffer, sizeof(buffer))) { - qWarning("QCoreTextFontDatabase::filenameForCFUrl: could not resolve file for URL %s", - url ? qPrintable(QString::fromCFString(CFURLGetString(url))) : "(null)"); - } else { - QCFType scheme = CFURLCopyScheme(url); - if (QString::fromCFString(scheme) == QLatin1String("qrc")) - filename = ":"; - - filename += reinterpret_cast(buffer); - } - - return filename; -} -#endif - extern CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef); template <> @@ -420,11 +396,16 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine(const { CTFontDescriptorRef descriptor = static_cast(usrPtr); - QCFType url(static_cast(CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute))); - QByteArray filename; - if (url) - filename = filenameForCFUrl(url); + if (NSURL *url = [static_cast(CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute)) autorelease]) { + if ([url.scheme isEqual:@"qrc"]) + filename = ":"; + else if (!url.fileURL) + qWarning() << "QFontDatabase: Unknown scheme" << url.scheme << "in font descriptor URL"; + + filename += QString::fromNSString(url.path).toUtf8(); + } + Q_ASSERT(!filename.isEmpty()); QFontEngine::FaceId faceId; faceId.filename = filename; -- cgit v1.2.3 From 1b2234c4e7d8f1ddb9f22312b4563da2db5ad303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 31 Mar 2017 16:48:43 +0200 Subject: macOS: Remove un-needed fontDef assignment in QCoreTextFontDatabase The QCoreTextFontEngine constructor already sets the member (like it should). Change-Id: I6e59ff36c439857418a636de4056c20d2f65e8d4 Reviewed-by: Simon Hausmann --- src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm | 1 - 1 file changed, 1 deletion(-) (limited to 'src/platformsupport/fontdatabases') diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index ea0aab17a0..ce9b61ba4c 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -382,7 +382,6 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory::fontEngine CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix); if (font) { QFontEngine *engine = new QCoreTextFontEngine(font, fontDef); - engine->fontDef = fontDef; CFRelease(font); return engine; } -- cgit v1.2.3 From b67b80b71516af71fec28732506c25ad338a84e7 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 4 Apr 2017 17:00:53 +0200 Subject: Fix stretch of QRawFonts Set stretch to always have no transform on raw fonts. Task-number: QTBUG-59799 Change-Id: Ibfacc5c247e0b4a8410572e207f09f6e67b74f9d Reviewed-by: Konstantin Tokarev Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/platformsupport/fontdatabases') diff --git a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp index 0826b0f2fb..8cf572b1af 100644 --- a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp @@ -152,6 +152,7 @@ QFontEngine *QBasicFontDatabase::fontEngine(const QByteArray &fontData, qreal pi { QFontDef fontDef; fontDef.pixelSize = pixelSize; + fontDef.stretch = QFont::Unstretched; fontDef.hintingPreference = hintingPreference; QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef); -- cgit v1.2.3 From efb84b6189f9e98c6dd29c22f00ad760445196c2 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 27 Mar 2017 15:48:37 +0200 Subject: Copy stretch to multifont fontDef If we do not the fontDef of the multifont will be the default 0. Task-number: QTBUG-59443 Change-Id: Ib223517975b2a57b2371e309d12cd8f918d30825 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/platformsupport/fontdatabases') diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp index 08ebbc3be0..5af73a6f2b 100644 --- a/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp @@ -1293,6 +1293,7 @@ QFontEngine *QWindowsMultiFontEngine::loadEngine(int at) fedw->fontDef.style = fontEngine->fontDef.style; fedw->fontDef.family = fam; fedw->fontDef.hintingPreference = fontEngine->fontDef.hintingPreference; + fedw->fontDef.stretch = fontEngine->fontDef.stretch; return fedw; } else { qErrnoWarning("%s: CreateFontFace failed", __FUNCTION__); @@ -1310,6 +1311,7 @@ QFontEngine *QWindowsMultiFontEngine::loadEngine(int at) fe->fontDef.style = fontEngine->fontDef.style; fe->fontDef.family = fam; fe->fontDef.hintingPreference = fontEngine->fontDef.hintingPreference; + fe->fontDef.stretch = fontEngine->fontDef.stretch; return fe; } -- cgit v1.2.3