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 +- .../android/qandroidplatformfontdatabase.cpp | 4 +- .../android/qandroidplatformfontdatabase.h | 4 +- .../platforms/minimal/qminimalintegration.cpp | 2 +- .../platforms/offscreen/qoffscreenintegration.cpp | 4 +- 23 files changed, 2975 insertions(+), 2975 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 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(); diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp index a14271c8f5..2fdf269566 100644 --- a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp +++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp @@ -66,7 +66,7 @@ void QAndroidPlatformFontDatabase::populateFontDatabase() const auto entries = dir.entryInfoList(nameFilters, QDir::Files); for (const QFileInfo &fi : entries) { const QByteArray file = QFile::encodeName(fi.absoluteFilePath()); - QBasicFontDatabase::addTTFile(QByteArray(), file); + QFreeTypeFontDatabase::addTTFile(QByteArray(), file); } } @@ -82,7 +82,7 @@ QStringList QAndroidPlatformFontDatabase::fallbacksForFamily(const QString &fami result.append(QString(qgetenv("QT_ANDROID_FONTS_SERIF")).split(QLatin1Char(';'))); else result.append(QString(qgetenv("QT_ANDROID_FONTS")).split(QLatin1Char(';'))); - result.append(QBasicFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + result.append(QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script)); return result; } diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.h b/src/plugins/platforms/android/qandroidplatformfontdatabase.h index 533d6e50a9..166a590698 100644 --- a/src/plugins/platforms/android/qandroidplatformfontdatabase.h +++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.h @@ -40,11 +40,11 @@ #ifndef QANDROIDPLATFORMFONTDATABASE_H #define QANDROIDPLATFORMFONTDATABASE_H -#include +#include QT_BEGIN_NAMESPACE -class QAndroidPlatformFontDatabase: public QBasicFontDatabase +class QAndroidPlatformFontDatabase: public QFreeTypeFontDatabase { public: QString fontDir() const override; diff --git a/src/plugins/platforms/minimal/qminimalintegration.cpp b/src/plugins/platforms/minimal/qminimalintegration.cpp index aa0037f187..820c4891ca 100644 --- a/src/plugins/platforms/minimal/qminimalintegration.cpp +++ b/src/plugins/platforms/minimal/qminimalintegration.cpp @@ -44,7 +44,7 @@ #include #include -#include +#include #if defined(Q_OS_WINRT) # include #elif defined(Q_OS_WIN) diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp index 56e6075cb2..0c39950019 100644 --- a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp +++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp @@ -49,7 +49,7 @@ #include #endif #elif defined(Q_OS_WIN) -#include +#include #ifndef Q_OS_WINRT #include #else @@ -103,7 +103,7 @@ QOffscreenIntegration::QOffscreenIntegration() m_fontDatabase.reset(new QGenericUnixFontDatabase()); #endif #elif defined(Q_OS_WIN) - m_fontDatabase.reset(new QBasicFontDatabase()); + m_fontDatabase.reset(new QFreeTypeFontDatabase()); #endif #ifndef QT_NO_DRAGANDDROP -- cgit v1.2.3