diff options
Diffstat (limited to 'src/platformsupport')
70 files changed, 6049 insertions, 249 deletions
diff --git a/src/platformsupport/accessibility/accessibility.pri b/src/platformsupport/accessibility/accessibility.pri deleted file mode 100644 index 924f6a512f..0000000000 --- a/src/platformsupport/accessibility/accessibility.pri +++ /dev/null @@ -1,9 +0,0 @@ -qtConfig(accessibility) { - INCLUDEPATH += $$PWD - - HEADERS += \ - $$PWD/qaccessiblebridgeutils_p.h - - SOURCES += \ - $$PWD/qaccessiblebridgeutils.cpp -} diff --git a/src/platformsupport/accessibility/accessibility.pro b/src/platformsupport/accessibility/accessibility.pro new file mode 100644 index 0000000000..5004dc8cbe --- /dev/null +++ b/src/platformsupport/accessibility/accessibility.pro @@ -0,0 +1,16 @@ +TARGET = QtAccessibilitySupport +MODULE = accessibility_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +HEADERS += \ + qaccessiblebridgeutils_p.h + +SOURCES += \ + qaccessiblebridgeutils.cpp + +load(qt_module) diff --git a/src/platformsupport/accessibility/qaccessiblebridgeutils.cpp b/src/platformsupport/accessibility/qaccessiblebridgeutils.cpp index 935cff3e6c..f280e65c29 100644 --- a/src/platformsupport/accessibility/qaccessiblebridgeutils.cpp +++ b/src/platformsupport/accessibility/qaccessiblebridgeutils.cpp @@ -39,8 +39,6 @@ #include "qaccessiblebridgeutils_p.h" #include <QtCore/qmath.h> -#ifndef QT_NO_ACCESSIBILITY - QT_BEGIN_NAMESPACE namespace QAccessibleBridgeUtils { @@ -113,5 +111,3 @@ bool performEffectiveAction(QAccessibleInterface *iface, const QString &actionNa } //namespace QT_END_NAMESPACE - -#endif diff --git a/src/platformsupport/accessibility/qaccessiblebridgeutils_p.h b/src/platformsupport/accessibility/qaccessiblebridgeutils_p.h index 79b153a749..cf8e126894 100644 --- a/src/platformsupport/accessibility/qaccessiblebridgeutils_p.h +++ b/src/platformsupport/accessibility/qaccessiblebridgeutils_p.h @@ -51,10 +51,12 @@ // We mean it. // +#include <QtGui/private/qtguiglobal_p.h> + #include <QtCore/qstringlist.h> #include <QtGui/qaccessible.h> -#ifndef QT_NO_ACCESSIBILITY +QT_REQUIRE_CONFIG(accessibility); QT_BEGIN_NAMESPACE @@ -65,6 +67,4 @@ namespace QAccessibleBridgeUtils { QT_END_NAMESPACE -#endif - #endif //QACCESSIBLEBRIDGEUTILS_H diff --git a/src/platformsupport/cglconvenience/cglconvenience.pri b/src/platformsupport/cglconvenience/cglconvenience.pri deleted file mode 100644 index 1de38bbd08..0000000000 --- a/src/platformsupport/cglconvenience/cglconvenience.pri +++ /dev/null @@ -1,11 +0,0 @@ -osx { - INCLUDEPATH += $$PWD - - HEADERS += \ - $$PWD/cglconvenience_p.h - - OBJECTIVE_SOURCES += \ - $$PWD/cglconvenience.mm - - LIBS_PRIVATE += -framework AppKit -framework OpenGL -} diff --git a/src/platformsupport/cglconvenience/cglconvenience.pro b/src/platformsupport/cglconvenience/cglconvenience.pro new file mode 100644 index 0000000000..0422a844aa --- /dev/null +++ b/src/platformsupport/cglconvenience/cglconvenience.pro @@ -0,0 +1,18 @@ +TARGET = QtCglSupport +MODULE = cgl_support + +QT = core-private gui +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +HEADERS += \ + cglconvenience_p.h + +OBJECTIVE_SOURCES += \ + cglconvenience.mm + +LIBS_PRIVATE += -framework AppKit -framework OpenGL + +load(qt_module) diff --git a/src/platformsupport/clipboard/clipboard.pri b/src/platformsupport/clipboard/clipboard.pri deleted file mode 100644 index cb8315d003..0000000000 --- a/src/platformsupport/clipboard/clipboard.pri +++ /dev/null @@ -1,7 +0,0 @@ -mac { - HEADERS += $$PWD/qmacmime_p.h - OBJECTIVE_SOURCES += $$PWD/qmacmime.mm - - osx: LIBS_PRIVATE += -framework AppKit -} - diff --git a/src/platformsupport/clipboard/clipboard.pro b/src/platformsupport/clipboard/clipboard.pro new file mode 100644 index 0000000000..336d81fe46 --- /dev/null +++ b/src/platformsupport/clipboard/clipboard.pro @@ -0,0 +1,15 @@ +TARGET = QtClipboardSupport +MODULE = clipboard_support + +QT = core-private gui +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +HEADERS += qmacmime_p.h +SOURCES += qmacmime.mm + +macos: LIBS_PRIVATE += -framework AppKit + +load(qt_module) diff --git a/src/platformsupport/clipboard/qmacmime.mm b/src/platformsupport/clipboard/qmacmime.mm index d48da8da15..6a6e033bec 100644 --- a/src/platformsupport/clipboard/qmacmime.mm +++ b/src/platformsupport/clipboard/qmacmime.mm @@ -414,8 +414,7 @@ QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, Q if (flavor == QLatin1String("public.utf8-plain-text")) { ret = QString::fromUtf8(firstData); } else if (flavor == QLatin1String("public.utf16-plain-text")) { - ret = QString(reinterpret_cast<const QChar *>(firstData.constData()), - firstData.size() / sizeof(QChar)); + ret = QTextCodec::codecForName("UTF-16")->toUnicode(firstData); } else { qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); } @@ -429,7 +428,7 @@ QList<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString & if (flavor == QLatin1String("public.utf8-plain-text")) ret.append(string.toUtf8()); else if (flavor == QLatin1String("public.utf16-plain-text")) - ret.append(QByteArray((char*)string.utf16(), string.length()*2)); + ret.append(QTextCodec::codecForName("UTF-16")->fromUnicode(string)); return ret; } diff --git a/src/platformsupport/devicediscovery/devicediscovery.pri b/src/platformsupport/devicediscovery/devicediscovery.pri deleted file mode 100644 index f4e9103130..0000000000 --- a/src/platformsupport/devicediscovery/devicediscovery.pri +++ /dev/null @@ -1,13 +0,0 @@ -HEADERS += $$PWD/qdevicediscovery_p.h - -qtConfig(libudev) { - SOURCES += $$PWD/qdevicediscovery_udev.cpp - HEADERS += $$PWD/qdevicediscovery_udev_p.h - QMAKE_USE_PRIVATE += libudev -} else: qtConfig(evdev) { - SOURCES += $$PWD/qdevicediscovery_static.cpp - HEADERS += $$PWD/qdevicediscovery_static_p.h -} else { - SOURCES += $$PWD/qdevicediscovery_dummy.cpp - HEADERS += $$PWD/qdevicediscovery_dummy_p.h -} diff --git a/src/platformsupport/devicediscovery/devicediscovery.pro b/src/platformsupport/devicediscovery/devicediscovery.pro new file mode 100644 index 0000000000..b429b8b97e --- /dev/null +++ b/src/platformsupport/devicediscovery/devicediscovery.pro @@ -0,0 +1,25 @@ +TARGET = QtDeviceDiscoverySupport +MODULE = devicediscovery_support + +QT = core-private +QT_FOR_CONFIG += gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +HEADERS += qdevicediscovery_p.h + +qtConfig(libudev) { + SOURCES += qdevicediscovery_udev.cpp + HEADERS += qdevicediscovery_udev_p.h + QMAKE_USE_PRIVATE += libudev +} else: qtConfig(evdev) { + SOURCES += qdevicediscovery_static.cpp + HEADERS += qdevicediscovery_static_p.h +} else { + SOURCES += qdevicediscovery_dummy.cpp + HEADERS += qdevicediscovery_dummy_p.h +} + +load(qt_module) diff --git a/src/platformsupport/eglconvenience/eglconvenience.pri b/src/platformsupport/eglconvenience/eglconvenience.pri deleted file mode 100644 index 4a93d997fb..0000000000 --- a/src/platformsupport/eglconvenience/eglconvenience.pri +++ /dev/null @@ -1,32 +0,0 @@ -qtConfig(egl) { - HEADERS += \ - $$PWD/qeglconvenience_p.h \ - $$PWD/qeglstreamconvenience_p.h \ - $$PWD/qt_egl_p.h - - SOURCES += \ - $$PWD/qeglconvenience.cpp \ - $$PWD/qeglstreamconvenience.cpp - - qtConfig(opengl) { - HEADERS += $$PWD/qeglplatformcontext_p.h \ - $$PWD/qeglpbuffer_p.h - - SOURCES += $$PWD/qeglplatformcontext.cpp \ - $$PWD/qeglpbuffer.cpp - } - - # Avoid X11 header collision, use generic EGL native types - DEFINES += QT_EGL_NO_X11 - - qtConfig(xlib) { - HEADERS += \ - $$PWD/qxlibeglintegration_p.h - SOURCES += \ - $$PWD/qxlibeglintegration.cpp - LIBS_PRIVATE += $$QMAKE_LIBS_X11 - } - CONFIG += egl - - LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD -} diff --git a/src/platformsupport/eglconvenience/eglconvenience.pro b/src/platformsupport/eglconvenience/eglconvenience.pro new file mode 100644 index 0000000000..d364a42b3b --- /dev/null +++ b/src/platformsupport/eglconvenience/eglconvenience.pro @@ -0,0 +1,43 @@ +TARGET = QtEglSupport +MODULE = egl_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +HEADERS += \ + qeglconvenience_p.h \ + qeglstreamconvenience_p.h \ + qt_egl_p.h + +SOURCES += \ + qeglconvenience.cpp \ + qeglstreamconvenience.cpp + +qtConfig(opengl) { + HEADERS += \ + qeglplatformcontext_p.h \ + qeglpbuffer_p.h + + SOURCES += \ + qeglplatformcontext.cpp \ + qeglpbuffer.cpp +} + +# Avoid X11 header collision, use generic EGL native types +DEFINES += QT_EGL_NO_X11 + +qtConfig(xlib) { + HEADERS += \ + qxlibeglintegration_p.h + SOURCES += \ + qxlibeglintegration.cpp + LIBS_PRIVATE += $$QMAKE_LIBS_X11 +} +CONFIG += egl + +LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD + +load(qt_module) diff --git a/src/platformsupport/eglconvenience/qeglconvenience_p.h b/src/platformsupport/eglconvenience/qeglconvenience_p.h index fdd21b8f19..ab2b813515 100644 --- a/src/platformsupport/eglconvenience/qeglconvenience_p.h +++ b/src/platformsupport/eglconvenience/qeglconvenience_p.h @@ -54,7 +54,7 @@ #include <QtGui/QSurfaceFormat> #include <QtCore/QVector> #include <QtCore/QSizeF> -#include <QtPlatformSupport/private/qt_egl_p.h> +#include <QtEglSupport/private/qt_egl_p.h> QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/eglconvenience/qeglpbuffer_p.h b/src/platformsupport/eglconvenience/qeglpbuffer_p.h index 4f9ea9d5f3..38370c0e62 100644 --- a/src/platformsupport/eglconvenience/qeglpbuffer_p.h +++ b/src/platformsupport/eglconvenience/qeglpbuffer_p.h @@ -52,7 +52,7 @@ // #include <qpa/qplatformoffscreensurface.h> -#include <QtPlatformSupport/private/qeglplatformcontext_p.h> +#include <QtEglSupport/private/qeglplatformcontext_p.h> QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/eglconvenience/qeglplatformcontext_p.h b/src/platformsupport/eglconvenience/qeglplatformcontext_p.h index f6b2b876f7..9d41eecd99 100644 --- a/src/platformsupport/eglconvenience/qeglplatformcontext_p.h +++ b/src/platformsupport/eglconvenience/qeglplatformcontext_p.h @@ -55,7 +55,7 @@ #include <qpa/qplatformwindow.h> #include <qpa/qplatformopenglcontext.h> #include <QtCore/QVariant> -#include <QtPlatformSupport/private/qt_egl_p.h> +#include <QtEglSupport/private/qt_egl_p.h> QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/eglconvenience/qeglstreamconvenience_p.h b/src/platformsupport/eglconvenience/qeglstreamconvenience_p.h index a4c8245280..6c84f29613 100644 --- a/src/platformsupport/eglconvenience/qeglstreamconvenience_p.h +++ b/src/platformsupport/eglconvenience/qeglstreamconvenience_p.h @@ -52,7 +52,7 @@ // #include <qglobal.h> -#include <QtPlatformSupport/private/qt_egl_p.h> +#include <QtEglSupport/private/qt_egl_p.h> // This provides runtime EGLDevice/Output/Stream support even when eglext.h in // the sysroot is not up-to-date. diff --git a/src/platformsupport/eventdispatchers/eventdispatchers.pri b/src/platformsupport/eventdispatchers/eventdispatchers.pri deleted file mode 100644 index fb0f3b3827..0000000000 --- a/src/platformsupport/eventdispatchers/eventdispatchers.pri +++ /dev/null @@ -1,21 +0,0 @@ -unix { -SOURCES +=\ - $$PWD/qunixeventdispatcher.cpp\ - $$PWD/qgenericunixeventdispatcher.cpp\ - -HEADERS +=\ - $$PWD/qunixeventdispatcher_qpa_p.h\ - $$PWD/qgenericunixeventdispatcher_p.h\ -} else: win32 { -SOURCES +=\ - $$PWD/qwindowsguieventdispatcher.cpp - -HEADERS +=\ - $$PWD/qwindowsguieventdispatcher_p.h -} - -qtConfig(glib) { - SOURCES +=$$PWD/qeventdispatcher_glib.cpp - HEADERS +=$$PWD/qeventdispatcher_glib_p.h - QMAKE_USE_PRIVATE += glib -} diff --git a/src/platformsupport/eventdispatchers/eventdispatchers.pro b/src/platformsupport/eventdispatchers/eventdispatchers.pro new file mode 100644 index 0000000000..9d3ac4bbc6 --- /dev/null +++ b/src/platformsupport/eventdispatchers/eventdispatchers.pro @@ -0,0 +1,32 @@ +TARGET = QtEventDispatcherSupport +MODULE = eventdispatcher_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +unix { + SOURCES += \ + qunixeventdispatcher.cpp \ + qgenericunixeventdispatcher.cpp + + HEADERS += \ + qunixeventdispatcher_qpa_p.h \ + qgenericunixeventdispatcher_p.h +} else { + SOURCES += \ + qwindowsguieventdispatcher.cpp + + HEADERS += \ + qwindowsguieventdispatcher_p.h +} + +qtConfig(glib) { + SOURCES += qeventdispatcher_glib.cpp + HEADERS += qeventdispatcher_glib_p.h + QMAKE_USE_PRIVATE += glib +} + +load(qt_module) diff --git a/src/platformsupport/fbconvenience/fbconvenience.pri b/src/platformsupport/fbconvenience/fbconvenience.pri deleted file mode 100644 index 4634f57fb4..0000000000 --- a/src/platformsupport/fbconvenience/fbconvenience.pri +++ /dev/null @@ -1,11 +0,0 @@ -SOURCES += $$PWD/qfbscreen.cpp \ - $$PWD/qfbbackingstore.cpp \ - $$PWD/qfbwindow.cpp \ - $$PWD/qfbcursor.cpp \ - $$PWD/qfbvthandler.cpp - -HEADERS += $$PWD/qfbscreen_p.h \ - $$PWD/qfbbackingstore_p.h \ - $$PWD/qfbwindow_p.h \ - $$PWD/qfbcursor_p.h \ - $$PWD/qfbvthandler_p.h diff --git a/src/platformsupport/fbconvenience/fbconvenience.pro b/src/platformsupport/fbconvenience/fbconvenience.pro new file mode 100644 index 0000000000..3775906470 --- /dev/null +++ b/src/platformsupport/fbconvenience/fbconvenience.pro @@ -0,0 +1,24 @@ +TARGET = QtFbSupport +MODULE = fb_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +SOURCES += \ + qfbscreen.cpp \ + qfbbackingstore.cpp \ + qfbwindow.cpp \ + qfbcursor.cpp \ + qfbvthandler.cpp + +HEADERS += \ + qfbscreen_p.h \ + qfbbackingstore_p.h \ + qfbwindow_p.h \ + qfbcursor_p.h \ + qfbvthandler_p.h + +load(qt_module) diff --git a/src/platformsupport/fontdatabases/basic/basic.pri b/src/platformsupport/fontdatabases/basic/basic.pri index 575c93fe20..c50dba3ce2 100644 --- a/src/platformsupport/fontdatabases/basic/basic.pri +++ b/src/platformsupport/fontdatabases/basic/basic.pri @@ -6,4 +6,4 @@ SOURCES += \ $$PWD/qbasicfontdatabase.cpp \ $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft.cpp -include($$QT_SOURCE_TREE/src/3rdparty/freetype_dependency.pri) +QMAKE_USE += freetype diff --git a/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri b/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri index 911f0c884d..6458464870 100644 --- a/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri +++ b/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri @@ -3,4 +3,4 @@ HEADERS += $$PWD/qfontconfigdatabase_p.h \ SOURCES += $$PWD/qfontconfigdatabase.cpp \ $$PWD/qfontenginemultifontconfig.cpp -QMAKE_USE += fontconfig/nolink +QMAKE_USE += fontconfig diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h index 244558b910..f7e3172b65 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h @@ -52,7 +52,7 @@ // #include <qpa/qplatformfontdatabase.h> -#include <QtPlatformSupport/private/qbasicfontdatabase_p.h> +#include <QtFontDatabaseSupport/private/qbasicfontdatabase_p.h> QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/fontdatabases/fontdatabases.pri b/src/platformsupport/fontdatabases/fontdatabases.pri deleted file mode 100644 index d435359141..0000000000 --- a/src/platformsupport/fontdatabases/fontdatabases.pri +++ /dev/null @@ -1,15 +0,0 @@ -darwin { - include($$PWD/mac/coretext.pri) -} else { - qtConfig(freetype) { - include($$PWD/basic/basic.pri) - } - - unix { - CONFIG += qpa/genericunixfontdatabase - include($$PWD/genericunix/genericunix.pri) - qtConfig(fontconfig) { - include($$PWD/fontconfig/fontconfig.pri) - } - } -} diff --git a/src/platformsupport/fontdatabases/fontdatabases.pro b/src/platformsupport/fontdatabases/fontdatabases.pro new file mode 100644 index 0000000000..f2c0ae4d96 --- /dev/null +++ b/src/platformsupport/fontdatabases/fontdatabases.pro @@ -0,0 +1,29 @@ +TARGET = QtFontDatabaseSupport +MODULE = fontdatabase_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +darwin { + include($$PWD/mac/coretext.pri) +} else { + qtConfig(freetype) { + include($$PWD/basic/basic.pri) + } + + unix { + include($$PWD/genericunix/genericunix.pri) + qtConfig(fontconfig) { + include($$PWD/fontconfig/fontconfig.pri) + } + } + + win32:!winrt { + include($$PWD/windows/windows.pri) + } +} + +load(qt_module) diff --git a/src/platformsupport/fontdatabases/genericunix/qgenericunixfontdatabase_p.h b/src/platformsupport/fontdatabases/genericunix/qgenericunixfontdatabase_p.h index 043f831530..37c667eeb3 100644 --- a/src/platformsupport/fontdatabases/genericunix/qgenericunixfontdatabase_p.h +++ b/src/platformsupport/fontdatabases/genericunix/qgenericunixfontdatabase_p.h @@ -51,13 +51,13 @@ // We mean it. // -#include <QtCore/qglobal.h> +#include <QtGui/private/qtguiglobal_p.h> -#ifdef Q_FONTCONFIGDATABASE -#include <QtPlatformSupport/private/qfontconfigdatabase_p.h> +#if QT_CONFIG(fontconfig) +#include <QtFontDatabaseSupport/private/qfontconfigdatabase_p.h> typedef QFontconfigDatabase QGenericUnixFontDatabase; #else -#include <QtPlatformSupport/private/qbasicfontdatabase_p.h> +#include <QtFontDatabaseSupport/private/qbasicfontdatabase_p.h> typedef QBasicFontDatabase QGenericUnixFontDatabase; #endif //Q_FONTCONFIGDATABASE diff --git a/src/platformsupport/fontdatabases/mac/coretext.pri b/src/platformsupport/fontdatabases/mac/coretext.pri index e1132cfcbe..aed89ad8c7 100644 --- a/src/platformsupport/fontdatabases/mac/coretext.pri +++ b/src/platformsupport/fontdatabases/mac/coretext.pri @@ -2,7 +2,7 @@ HEADERS += $$PWD/qcoretextfontdatabase_p.h $$PWD/qfontengine_coretext_p.h OBJECTIVE_SOURCES += $$PWD/qfontengine_coretext.mm $$PWD/qcoretextfontdatabase.mm qtConfig(freetype) { - include($$QT_SOURCE_TREE/src/3rdparty/freetype_dependency.pri) + QMAKE_USE += freetype HEADERS += $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft_p.h SOURCES += $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft.cpp } diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp new file mode 100644 index 0000000000..434aa16d16 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp @@ -0,0 +1,2006 @@ +/**************************************************************************** +** +** 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 "qwindowsfontdatabase_p.h" +#include "qwindowsfontdatabase_ft_p.h" // for default font +#include "qwindowsfontengine_p.h" +#include "qwindowsfontenginedirectwrite_p.h" +#include <QtCore/qt_windows.h> + +#include <QtGui/QFont> +#include <QtGui/QGuiApplication> +#include <QtGui/private/qhighdpiscaling_p.h> + +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QtEndian> +#include <QtCore/QThreadStorage> +#include <QtCore/private/qsystemlibrary_p.h> + +#include <wchar.h> + +#if !defined(QT_NO_DIRECTWRITE) +# if defined(QT_USE_DIRECTWRITE2) +# include <dwrite_2.h> +# else +# include <dwrite.h> +# endif +# include <d2d1.h> +#endif + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQpaFonts, "qt.qpa.fonts") + +#ifndef QT_NO_DIRECTWRITE +// ### fixme: Consider direct linking of dwrite.dll once Windows Vista pre SP2 is dropped (QTBUG-49711) + +typedef HRESULT (WINAPI *DWriteCreateFactoryType)(DWRITE_FACTORY_TYPE, const IID &, IUnknown **); + +static inline DWriteCreateFactoryType resolveDWriteCreateFactory() +{ + if (QSysInfo::windowsVersion() < QSysInfo::WV_VISTA) + return Q_NULLPTR; + QSystemLibrary library(QStringLiteral("dwrite")); + QFunctionPointer result = library.resolve("DWriteCreateFactory"); + if (Q_UNLIKELY(!result)) { + qWarning("Unable to load dwrite.dll"); + return Q_NULLPTR; + } + return reinterpret_cast<DWriteCreateFactoryType>(result); +} + +static void createDirectWriteFactory(IDWriteFactory **factory) +{ + *factory = Q_NULLPTR; + + static const DWriteCreateFactoryType dWriteCreateFactory = resolveDWriteCreateFactory(); + if (!dWriteCreateFactory) + return; + + IUnknown *result = NULL; +#if defined(QT_USE_DIRECTWRITE2) + dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result); +#endif + + if (result == NULL) { + if (FAILED(dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) { + qErrnoWarning("DWriteCreateFactory failed"); + return; + } + } + + *factory = static_cast<IDWriteFactory *>(result); +} + +static inline bool useDirectWrite(QFont::HintingPreference hintingPreference, + const QString &familyName = QString(), + bool isColorFont = false) +{ + const unsigned options = QWindowsFontDatabase::fontOptions(); + if (Q_UNLIKELY(options & QWindowsFontDatabase::DontUseDirectWriteFonts)) + return false; + + // At some scales, GDI will misrender the MingLiU font, so we force use of + // DirectWrite to work around the issue. + if (Q_UNLIKELY(familyName.startsWith(QLatin1String("MingLiU")))) + return true; + + if (isColorFont) + return (options & QWindowsFontDatabase::DontUseColorFonts) == 0; + + return hintingPreference == QFont::PreferNoHinting + || hintingPreference == QFont::PreferVerticalHinting + || (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting); +} +#endif // !QT_NO_DIRECTWRITE + +// Helper classes for creating font engines directly from font data +namespace { + +# pragma pack(1) + + // Common structure for all formats of the "name" table + struct NameTable + { + quint16 format; + quint16 count; + quint16 stringOffset; + }; + + struct NameRecord + { + quint16 platformID; + quint16 encodingID; + quint16 languageID; + quint16 nameID; + quint16 length; + quint16 offset; + }; + + struct OffsetSubTable + { + quint32 scalerType; + quint16 numTables; + quint16 searchRange; + quint16 entrySelector; + quint16 rangeShift; + }; + + struct TableDirectory + { + quint32 identifier; + quint32 checkSum; + quint32 offset; + quint32 length; + }; + + struct OS2Table + { + quint16 version; + qint16 avgCharWidth; + quint16 weightClass; + quint16 widthClass; + quint16 type; + qint16 subscriptXSize; + qint16 subscriptYSize; + qint16 subscriptXOffset; + qint16 subscriptYOffset; + qint16 superscriptXSize; + qint16 superscriptYSize; + qint16 superscriptXOffset; + qint16 superscriptYOffset; + qint16 strikeOutSize; + qint16 strikeOutPosition; + qint16 familyClass; + quint8 panose[10]; + quint32 unicodeRanges[4]; + quint8 vendorID[4]; + quint16 selection; + quint16 firstCharIndex; + quint16 lastCharIndex; + qint16 typoAscender; + qint16 typoDescender; + qint16 typoLineGap; + quint16 winAscent; + quint16 winDescent; + quint32 codepageRanges[2]; + qint16 height; + qint16 capHeight; + quint16 defaultChar; + quint16 breakChar; + quint16 maxContext; + }; + +# pragma pack() + + class EmbeddedFont + { + public: + EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) {} + + QString changeFamilyName(const QString &newFamilyName); + QByteArray data() const { return m_fontData; } + TableDirectory *tableDirectoryEntry(const QByteArray &tagName); + QString familyName(TableDirectory *nameTableDirectory = 0); + + private: + QByteArray m_fontData; + }; + + TableDirectory *EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName) + { + Q_ASSERT(tagName.size() == 4); + quint32 tagId = *(reinterpret_cast<const quint32 *>(tagName.constData())); + const size_t fontDataSize = m_fontData.size(); + if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable))) + return 0; + + OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data()); + TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1); + + const size_t tableCount = qFromBigEndian<quint16>(offsetSubTable->numTables); + if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable) + sizeof(TableDirectory) * tableCount)) + return 0; + + TableDirectory *tableDirectoryEnd = tableDirectory + tableCount; + for (TableDirectory *entry = tableDirectory; entry < tableDirectoryEnd; ++entry) { + if (entry->identifier == tagId) + return entry; + } + + return 0; + } + + QString EmbeddedFont::familyName(TableDirectory *nameTableDirectoryEntry) + { + QString name; + + if (nameTableDirectoryEntry == 0) + nameTableDirectoryEntry = tableDirectoryEntry("name"); + + if (nameTableDirectoryEntry != 0) { + quint32 offset = qFromBigEndian<quint32>(nameTableDirectoryEntry->offset); + if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameTable))) + return QString(); + + NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() + offset); + NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); + + quint16 nameTableCount = qFromBigEndian<quint16>(nameTable->count); + if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameRecord) * nameTableCount)) + return QString(); + + for (int i = 0; i < nameTableCount; ++i, ++nameRecord) { + if (qFromBigEndian<quint16>(nameRecord->nameID) == 1 + && qFromBigEndian<quint16>(nameRecord->platformID) == 3 // Windows + && qFromBigEndian<quint16>(nameRecord->languageID) == 0x0409) { // US English + quint16 stringOffset = qFromBigEndian<quint16>(nameTable->stringOffset); + quint16 nameOffset = qFromBigEndian<quint16>(nameRecord->offset); + quint16 nameLength = qFromBigEndian<quint16>(nameRecord->length); + + if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + stringOffset + nameOffset + nameLength)) + return QString(); + + const void *ptr = reinterpret_cast<const quint8 *>(nameTable) + + stringOffset + + nameOffset; + + const quint16 *s = reinterpret_cast<const quint16 *>(ptr); + const quint16 *e = s + nameLength / sizeof(quint16); + while (s != e) + name += QChar( qFromBigEndian<quint16>(*s++)); + break; + } + } + } + + return name; + } + + QString EmbeddedFont::changeFamilyName(const QString &newFamilyName) + { + TableDirectory *nameTableDirectoryEntry = tableDirectoryEntry("name"); + if (nameTableDirectoryEntry == 0) + return QString(); + + QString oldFamilyName = familyName(nameTableDirectoryEntry); + + // Reserve size for name table header, five required name records and string + const int requiredRecordCount = 5; + quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 }; + + int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount; + int newFamilyNameSize = newFamilyName.size() * int(sizeof(quint16)); + + const QString regularString = QString::fromLatin1("Regular"); + int regularStringSize = regularString.size() * int(sizeof(quint16)); + + // Align table size of table to 32 bits (pad with 0) + int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4; + + QByteArray newNameTable(fullSize, char(0)); + + { + NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data()); + nameTable->count = qbswap<quint16>(requiredRecordCount); + nameTable->stringOffset = qbswap<quint16>(sizeOfHeader); + + NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); + for (int i = 0; i < requiredRecordCount; ++i, nameRecord++) { + nameRecord->nameID = qbswap<quint16>(nameIds[i]); + nameRecord->encodingID = qbswap<quint16>(1); + nameRecord->languageID = qbswap<quint16>(0x0409); + nameRecord->platformID = qbswap<quint16>(3); + nameRecord->length = qbswap<quint16>(newFamilyNameSize); + + // Special case for sub-family + if (nameIds[i] == 4) { + nameRecord->offset = qbswap<quint16>(newFamilyNameSize); + nameRecord->length = qbswap<quint16>(regularStringSize); + } + } + + // nameRecord now points to string data + quint16 *stringStorage = reinterpret_cast<quint16 *>(nameRecord); + const quint16 *sourceString = newFamilyName.utf16(); + for (int i = 0; i < newFamilyName.size(); ++i) + stringStorage[i] = qbswap<quint16>(sourceString[i]); + stringStorage += newFamilyName.size(); + + sourceString = regularString.utf16(); + for (int i = 0; i < regularString.size(); ++i) + stringStorage[i] = qbswap<quint16>(sourceString[i]); + } + + quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data()); + quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize); + + quint32 checkSum = 0; + while (p < tableEnd) + checkSum += qFromBigEndian<quint32>(*(p++)); + + nameTableDirectoryEntry->checkSum = qbswap<quint32>(checkSum); + nameTableDirectoryEntry->offset = qbswap<quint32>(m_fontData.size()); + nameTableDirectoryEntry->length = qbswap<quint32>(fullSize); + + m_fontData.append(newNameTable); + + return oldFamilyName; + } + +#if !defined(QT_NO_DIRECTWRITE) + + class DirectWriteFontFileStream: public IDWriteFontFileStream + { + public: + DirectWriteFontFileStream(const QByteArray &fontData) + : m_fontData(fontData) + , m_referenceCount(0) + { + } + virtual ~DirectWriteFontFileStream() + { + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset, + UINT64 fragmentSize, OUT void **fragmentContext); + void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext); + HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize); + HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime); + + private: + QByteArray m_fontData; + ULONG m_referenceCount; + }; + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) { + *object = this; + AddRef(); + return S_OK; + } else { + *object = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef() + { + return InterlockedIncrement(&m_referenceCount); + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release() + { + ULONG newCount = InterlockedDecrement(&m_referenceCount); + if (newCount == 0) + delete this; + return newCount; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment( + const void **fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + OUT void **fragmentContext) + { + *fragmentContext = NULL; + if (fileOffset + fragmentSize <= quint64(m_fontData.size())) { + *fragmentStart = m_fontData.data() + fileOffset; + return S_OK; + } else { + *fragmentStart = NULL; + return E_FAIL; + } + } + + void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *) + { + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize) + { + *fileSize = m_fontData.size(); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) + { + *lastWriteTime = 0; + return E_NOTIMPL; + } + + class DirectWriteFontFileLoader: public IDWriteFontFileLoader + { + public: + DirectWriteFontFileLoader() : m_referenceCount(0) {} + virtual ~DirectWriteFontFileLoader() + { + } + + inline void addKey(const void *key, const QByteArray &fontData) + { + Q_ASSERT(!m_fontDatas.contains(key)); + m_fontDatas.insert(key, fontData); + } + + inline void removeKey(const void *key) + { + m_fontDatas.remove(key); + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + OUT IDWriteFontFileStream **fontFileStream); + + private: + ULONG m_referenceCount; + QHash<const void *, QByteArray> m_fontDatas; + }; + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid, + void **object) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { + *object = this; + AddRef(); + return S_OK; + } else { + *object = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef() + { + return InterlockedIncrement(&m_referenceCount); + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release() + { + ULONG newCount = InterlockedDecrement(&m_referenceCount); + if (newCount == 0) + delete this; + return newCount; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey( + void const *fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + IDWriteFontFileStream **fontFileStream) + { + Q_UNUSED(fontFileReferenceKeySize); + + if (fontFileReferenceKeySize != sizeof(const void *)) { + qWarning("%s: Wrong key size", __FUNCTION__); + return E_FAIL; + } + + const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey); + *fontFileStream = NULL; + if (!m_fontDatas.contains(key)) + return E_FAIL; + + QByteArray fontData = m_fontDatas.value(key); + DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData); + stream->AddRef(); + *fontFileStream = stream; + + return S_OK; + } + + class CustomFontFileLoader + { + public: + CustomFontFileLoader() : m_directWriteFontFileLoader(Q_NULLPTR) + { + createDirectWriteFactory(&m_directWriteFactory); + + if (m_directWriteFactory) { + m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); + m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); + } + } + + ~CustomFontFileLoader() + { + if (m_directWriteFactory != 0 && m_directWriteFontFileLoader != 0) + m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); + + if (m_directWriteFactory != 0) + m_directWriteFactory->Release(); + } + + void addKey(const void *key, const QByteArray &fontData) + { + if (m_directWriteFontFileLoader != 0) + m_directWriteFontFileLoader->addKey(key, fontData); + } + + void removeKey(const void *key) + { + if (m_directWriteFontFileLoader != 0) + m_directWriteFontFileLoader->removeKey(key); + } + + IDWriteFontFileLoader *loader() const + { + return m_directWriteFontFileLoader; + } + + private: + IDWriteFactory *m_directWriteFactory; + DirectWriteFontFileLoader *m_directWriteFontFileLoader; + }; + +#endif + +} // Anonymous namespace + +/*! + \struct QWindowsFontEngineData + \brief Static constant data shared by the font engines. + \ingroup qt-lighthouse-win +*/ + +QWindowsFontEngineData::QWindowsFontEngineData() + : clearTypeEnabled(false) + , fontSmoothingGamma(QWindowsFontDatabase::fontSmoothingGamma()) +#if !defined(QT_NO_DIRECTWRITE) + , directWriteFactory(0) + , directWriteGdiInterop(0) +#endif +{ + // from qapplication_win.cpp + UINT result = 0; + if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0)) + clearTypeEnabled = (result == FE_FONTSMOOTHINGCLEARTYPE); + + const qreal gray_gamma = 2.31; + for (int i=0; i<256; ++i) + pow_gamma[i] = uint(qRound(qPow(i / qreal(255.), gray_gamma) * 2047)); + + HDC displayDC = GetDC(0); + hdc = CreateCompatibleDC(displayDC); + ReleaseDC(0, displayDC); +} + +unsigned QWindowsFontDatabase::m_fontOptions = 0; + +void QWindowsFontDatabase::setFontOptions(unsigned options) +{ + m_fontOptions = options & (QWindowsFontDatabase::DontUseDirectWriteFonts | + QWindowsFontDatabase::DontUseColorFonts); +} + +unsigned QWindowsFontDatabase::fontOptions() +{ + return m_fontOptions; +} + +QWindowsFontEngineData::~QWindowsFontEngineData() +{ + if (hdc) + DeleteDC(hdc); +#if !defined(QT_NO_DIRECTWRITE) + if (directWriteGdiInterop) + directWriteGdiInterop->Release(); + if (directWriteFactory) + directWriteFactory->Release(); +#endif +} + +qreal QWindowsFontDatabase::fontSmoothingGamma() +{ + int winSmooth; + qreal result = 1; + if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) + result = qreal(winSmooth) / qreal(1000.0); + + // Safeguard ourselves against corrupt registry values... + if (result > 5 || result < 1) + result = qreal(1.4); + return result; +} + +#if !defined(QT_NO_DIRECTWRITE) +static inline bool initDirectWrite(QWindowsFontEngineData *d) +{ + if (!d->directWriteFactory) { + createDirectWriteFactory(&d->directWriteFactory); + if (!d->directWriteFactory) + return false; + } + if (!d->directWriteGdiInterop) { + const HRESULT hr = d->directWriteFactory->GetGdiInterop(&d->directWriteGdiInterop); + if (FAILED(hr)) { + qErrnoWarning("%s: GetGdiInterop failed", __FUNCTION__); + return false; + } + } + return true; +} + +#endif // !defined(QT_NO_DIRECTWRITE) + +/*! + \class QWindowsFontDatabase + \brief Font database for Windows + + \note The Qt 4.8 WIndows font database employed a mechanism of + delayed population of the database again passing a font name + to EnumFontFamiliesEx(), working around the fact that + EnumFontFamiliesEx() does not list all fonts by default. + This should be introduced to Lighthouse as well? + + \internal + \ingroup qt-lighthouse-win +*/ + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const QFontDef &def) +{ + QDebugStateSaver saver(d); + d.nospace(); + d.noquote(); + d << "QFontDef(Family=\"" << def.family << '"'; + if (!def.styleName.isEmpty()) + d << ", stylename=" << def.styleName; + d << ", pointsize=" << def.pointSize << ", pixelsize=" << def.pixelSize + << ", styleHint=" << def.styleHint << ", weight=" << def.weight + << ", stretch=" << def.stretch << ", hintingPreference=" + << def.hintingPreference << ')'; + return d; +} + +QDebug operator<<(QDebug d, const LOGFONT &lf) +{ + QDebugStateSaver saver(d); + d.nospace(); + d.noquote(); + d << "LOGFONT(\"" << QString::fromWCharArray(lf.lfFaceName) + << "\", lfWidth=" << lf.lfWidth << ", lfHeight=" << lf.lfHeight << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + +static inline QFontDatabase::WritingSystem writingSystemFromCharSet(uchar charSet) +{ + switch (charSet) { + case ANSI_CHARSET: + case EASTEUROPE_CHARSET: + case BALTIC_CHARSET: + case TURKISH_CHARSET: + return QFontDatabase::Latin; + case GREEK_CHARSET: + return QFontDatabase::Greek; + case RUSSIAN_CHARSET: + return QFontDatabase::Cyrillic; + case HEBREW_CHARSET: + return QFontDatabase::Hebrew; + case ARABIC_CHARSET: + return QFontDatabase::Arabic; + case THAI_CHARSET: + return QFontDatabase::Thai; + case GB2312_CHARSET: + return QFontDatabase::SimplifiedChinese; + case CHINESEBIG5_CHARSET: + return QFontDatabase::TraditionalChinese; + case SHIFTJIS_CHARSET: + return QFontDatabase::Japanese; + case HANGUL_CHARSET: + case JOHAB_CHARSET: + return QFontDatabase::Korean; + case VIETNAMESE_CHARSET: + return QFontDatabase::Vietnamese; + case SYMBOL_CHARSET: + return QFontDatabase::Symbol; + default: + break; + } + return QFontDatabase::Any; +} + +#ifdef MAKE_TAG +#undef MAKE_TAG +#endif +// GetFontData expects the tags in little endian ;( +#define MAKE_TAG(ch1, ch2, ch3, ch4) (\ + (((quint32)(ch4)) << 24) | \ + (((quint32)(ch3)) << 16) | \ + (((quint32)(ch2)) << 8) | \ + ((quint32)(ch1)) \ + ) + +bool localizedName(const QString &name) +{ + const QChar *c = name.unicode(); + for (int i = 0; i < name.length(); ++i) { + if (c[i].unicode() >= 0x100) + return true; + } + return false; +} + +static inline quint16 getUShort(const unsigned char *p) +{ + quint16 val; + val = *p++ << 8; + val |= *p; + + return val; +} + +namespace { + +struct FontNames { + QString name; // e.g. "DejaVu Sans Condensed" + QString style; // e.g. "Italic" + QString preferredName; // e.g. "DejaVu Sans" + QString preferredStyle; // e.g. "Condensed Italic" +}; + +static QString readName(bool unicode, const uchar *string, int length) +{ + QString out; + if (unicode) { + // utf16 + + length /= 2; + out.resize(length); + QChar *uc = out.data(); + for (int i = 0; i < length; ++i) + uc[i] = getUShort(string + 2*i); + } else { + // Apple Roman + + out.resize(length); + QChar *uc = out.data(); + for (int i = 0; i < length; ++i) + uc[i] = QLatin1Char(char(string[i])); + } + return out; +} + +enum FieldTypeValue { + FamilyId = 1, + StyleId = 2, + PreferredFamilyId = 16, + PreferredStyleId = 17, +}; + +enum PlatformFieldValue { + PlatformId_Unicode = 0, + PlatformId_Apple = 1, + PlatformId_Microsoft = 3 +}; + +static FontNames getCanonicalFontNames(const uchar *table, quint32 bytes) +{ + FontNames out; + const int NameRecordSize = 12; + const int MS_LangIdEnglish = 0x009; + + // get the name table + quint16 count; + quint16 string_offset; + const unsigned char *names; + + if (bytes < 8) + return out; + + if (getUShort(table) != 0) + return out; + + count = getUShort(table+2); + string_offset = getUShort(table+4); + names = table + 6; + + if (string_offset >= bytes || 6 + count*NameRecordSize > string_offset) + return out; + + enum PlatformIdType { + NotFound = 0, + Unicode = 1, + Apple = 2, + Microsoft = 3 + }; + + PlatformIdType idStatus[4] = { NotFound, NotFound, NotFound, NotFound }; + int ids[4] = { -1, -1, -1, -1 }; + + for (int i = 0; i < count; ++i) { + // search for the correct name entries + + quint16 platform_id = getUShort(names + i*NameRecordSize); + quint16 encoding_id = getUShort(names + 2 + i*NameRecordSize); + quint16 language_id = getUShort(names + 4 + i*NameRecordSize); + quint16 name_id = getUShort(names + 6 + i*NameRecordSize); + + PlatformIdType *idType = nullptr; + int *id = nullptr; + + switch (name_id) { + case FamilyId: + idType = &idStatus[0]; + id = &ids[0]; + break; + case StyleId: + idType = &idStatus[1]; + id = &ids[1]; + break; + case PreferredFamilyId: + idType = &idStatus[2]; + id = &ids[2]; + break; + case PreferredStyleId: + idType = &idStatus[3]; + id = &ids[3]; + break; + default: + continue; + } + + quint16 length = getUShort(names + 8 + i*NameRecordSize); + quint16 offset = getUShort(names + 10 + i*NameRecordSize); + if (DWORD(string_offset + offset + length) > bytes) + continue; + + if ((platform_id == PlatformId_Microsoft + && (encoding_id == 0 || encoding_id == 1)) + && (language_id & 0x3ff) == MS_LangIdEnglish + && *idType < Microsoft) { + *id = i; + *idType = Microsoft; + } + // not sure if encoding id 4 for Unicode is utf16 or ucs4... + else if (platform_id == PlatformId_Unicode && encoding_id < 4 && *idType < Unicode) { + *id = i; + *idType = Unicode; + } + else if (platform_id == PlatformId_Apple && encoding_id == 0 && language_id == 0 && *idType < Apple) { + *id = i; + *idType = Apple; + } + } + + QString strings[4]; + for (int i = 0; i < 4; ++i) { + if (idStatus[i] == NotFound) + continue; + int id = ids[i]; + quint16 length = getUShort(names + 8 + id * NameRecordSize); + quint16 offset = getUShort(names + 10 + id * NameRecordSize); + const unsigned char *string = table + string_offset + offset; + strings[i] = readName(idStatus[i] != Apple, string, length); + } + + out.name = strings[0]; + out.style = strings[1]; + out.preferredName = strings[2]; + out.preferredStyle = strings[3]; + return out; +} + +} // namespace + +QString getEnglishName(const QString &familyName, bool includeStyle = false) +{ + QString i18n_name; + QString faceName = familyName; + faceName.truncate(LF_FACESIZE - 1); + + HDC hdc = GetDC( 0 ); + LOGFONT lf; + memset(&lf, 0, sizeof(LOGFONT)); + faceName.toWCharArray(lf.lfFaceName); + lf.lfFaceName[faceName.size()] = 0; + lf.lfCharSet = DEFAULT_CHARSET; + HFONT hfont = CreateFontIndirect(&lf); + + if (!hfont) { + ReleaseDC(0, hdc); + return QString(); + } + + HGDIOBJ oldobj = SelectObject( hdc, hfont ); + + const DWORD name_tag = MAKE_TAG( 'n', 'a', 'm', 'e' ); + + // get the name table + unsigned char *table = 0; + + DWORD bytes = GetFontData( hdc, name_tag, 0, 0, 0 ); + if ( bytes == GDI_ERROR ) { + // ### Unused variable + // int err = GetLastError(); + goto error; + } + + table = new unsigned char[bytes]; + GetFontData(hdc, name_tag, 0, table, bytes); + if ( bytes == GDI_ERROR ) + goto error; + + { + const FontNames names = getCanonicalFontNames(table, bytes); + i18n_name = names.name; + if (includeStyle) + i18n_name += QLatin1Char(' ') + names.style; + } +error: + delete [] table; + SelectObject( hdc, oldobj ); + DeleteObject( hfont ); + ReleaseDC( 0, hdc ); + + //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data()); + return i18n_name; +} + +static bool addFontToDatabase(const QString &familyName, const QString &styleName, uchar charSet, + const TEXTMETRIC *textmetric, + const FONTSIGNATURE *signature, + int type, + bool registerAlias) +{ + // the "@family" fonts are just the same as "family". Ignore them. + if (familyName.isEmpty() || familyName.at(0) == QLatin1Char('@') || familyName.startsWith(QLatin1String("WST_"))) + return false; + + static const int SMOOTH_SCALABLE = 0xffff; + const QString foundryName; // No such concept. + const bool fixed = !(textmetric->tmPitchAndFamily & TMPF_FIXED_PITCH); + const bool ttf = (textmetric->tmPitchAndFamily & TMPF_TRUETYPE); + const bool scalable = textmetric->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE); + const int size = scalable ? SMOOTH_SCALABLE : textmetric->tmHeight; + const QFont::Style style = textmetric->tmItalic ? QFont::StyleItalic : QFont::StyleNormal; + const bool antialias = false; + const QFont::Weight weight = QPlatformFontDatabase::weightFromInteger(textmetric->tmWeight); + const QFont::Stretch stretch = QFont::Unstretched; + +#ifndef QT_NO_DEBUG_OUTPUT + if (lcQpaFonts().isDebugEnabled()) { + QString message; + QTextStream str(&message); + str << __FUNCTION__ << ' ' << familyName << ' ' << charSet << " TTF=" << ttf; + if (type & DEVICE_FONTTYPE) + str << " DEVICE"; + if (type & RASTER_FONTTYPE) + str << " RASTER"; + if (type & TRUETYPE_FONTTYPE) + str << " TRUETYPE"; + str << " scalable=" << scalable << " Size=" << size + << " Style=" << style << " Weight=" << weight + << " stretch=" << stretch; + qCDebug(lcQpaFonts) << message; + } +#endif + + QString englishName; + if (registerAlias && ttf && localizedName(familyName)) + englishName = getEnglishName(familyName); + + QSupportedWritingSystems writingSystems; + if (type & TRUETYPE_FONTTYPE) { + Q_ASSERT(signature); + quint32 unicodeRange[4] = { + signature->fsUsb[0], signature->fsUsb[1], + signature->fsUsb[2], signature->fsUsb[3] + }; + quint32 codePageRange[2] = { + signature->fsCsb[0], signature->fsCsb[1] + }; + writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + // ### Hack to work around problem with Thai text on Windows 7. Segoe UI contains + // the symbol for Baht, and Windows thus reports that it supports the Thai script. + // Since it's the default UI font on this platform, most widgets will be unable to + // display Thai text by default. As a temporary work around, we special case Segoe UI + // and remove the Thai script from its list of supported writing systems. + if (writingSystems.supported(QFontDatabase::Thai) && + familyName == QLatin1String("Segoe UI")) + writingSystems.setSupported(QFontDatabase::Thai, false); + } else { + const QFontDatabase::WritingSystem ws = writingSystemFromCharSet(charSet); + if (ws != QFontDatabase::Any) + writingSystems.setSupported(ws); + } + + QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, + style, stretch, antialias, scalable, size, fixed, writingSystems, 0); + // add fonts windows can generate for us: + if (weight <= QFont::DemiBold && styleName.isEmpty()) + QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, + style, stretch, antialias, scalable, size, fixed, writingSystems, 0); + if (style != QFont::StyleItalic && styleName.isEmpty()) + QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, + QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, 0); + if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty()) + QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, + QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, 0); + + if (!englishName.isEmpty()) + QPlatformFontDatabase::registerAliasToFontFamily(familyName, englishName); + + return true; +} + +static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *textmetric, + DWORD type, LPARAM lParam) +{ + const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont); + const QString familyName = QString::fromWCharArray(f->elfLogFont.lfFaceName); + const QString styleName = QString::fromWCharArray(f->elfStyle); + const uchar charSet = f->elfLogFont.lfCharSet; + const bool registerAlias = bool(lParam); + + // NEWTEXTMETRICEX (passed for TT fonts) is a NEWTEXTMETRIC, which according + // to the documentation is identical to a TEXTMETRIC except for the last four + // members, which we don't use anyway + const FONTSIGNATURE *signature = Q_NULLPTR; + if (type & TRUETYPE_FONTTYPE) + signature = &reinterpret_cast<const NEWTEXTMETRICEX *>(textmetric)->ntmFontSig; + addFontToDatabase(familyName, styleName, charSet, textmetric, signature, type, registerAlias); + + // keep on enumerating + return 1; +} + +void QWindowsFontDatabase::populateFamily(const QString &familyName, bool registerAlias) +{ + qCDebug(lcQpaFonts) << familyName; + if (familyName.size() >= LF_FACESIZE) { + qCWarning(lcQpaFonts) << "Unable to enumerate family '" << familyName << '\''; + return; + } + HDC dummy = GetDC(0); + LOGFONT lf; + lf.lfCharSet = DEFAULT_CHARSET; + familyName.toWCharArray(lf.lfFaceName); + lf.lfFaceName[familyName.size()] = 0; + lf.lfPitchAndFamily = 0; + EnumFontFamiliesEx(dummy, &lf, storeFont, LPARAM(registerAlias), 0); + ReleaseDC(0, dummy); +} + +void QWindowsFontDatabase::populateFamily(const QString &familyName) +{ + populateFamily(familyName, false); +} + +namespace { +// Context for enumerating system fonts, records whether the default font has been encountered, +// which is normally not enumerated by EnumFontFamiliesEx(). +struct PopulateFamiliesContext +{ + PopulateFamiliesContext(const QString &f) : systemDefaultFont(f), seenSystemDefaultFont(false) {} + + QString systemDefaultFont; + bool seenSystemDefaultFont; +}; +} // namespace + +static int QT_WIN_CALLBACK populateFontFamilies(const LOGFONT *logFont, const TEXTMETRIC *textmetric, + DWORD, LPARAM lparam) +{ + // the "@family" fonts are just the same as "family". Ignore them. + const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont); + const wchar_t *faceNameW = f->elfLogFont.lfFaceName; + if (faceNameW[0] && faceNameW[0] != L'@' && wcsncmp(faceNameW, L"WST_", 4)) { + const QString faceName = QString::fromWCharArray(faceNameW); + QPlatformFontDatabase::registerFontFamily(faceName); + PopulateFamiliesContext *context = reinterpret_cast<PopulateFamiliesContext *>(lparam); + if (!context->seenSystemDefaultFont && faceName == context->systemDefaultFont) + context->seenSystemDefaultFont = true; + + // Register current font's english name as alias + const bool ttf = textmetric->tmPitchAndFamily & TMPF_TRUETYPE; + if (ttf && localizedName(faceName)) { + const QString englishName = getEnglishName(faceName); + if (!englishName.isEmpty()) { + QPlatformFontDatabase::registerAliasToFontFamily(faceName, englishName); + // Check whether the system default font name is an alias of the current font family name, + // as on Chinese Windows, where the system font "SimSun" is an alias to a font registered under a local name + if (!context->seenSystemDefaultFont && englishName == context->systemDefaultFont) + context->seenSystemDefaultFont = true; + } + } + } + return 1; // continue +} + +void QWindowsFontDatabase::populateFontDatabase() +{ + removeApplicationFonts(); + HDC dummy = GetDC(0); + LOGFONT lf; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfFaceName[0] = 0; + lf.lfPitchAndFamily = 0; + PopulateFamiliesContext context(QWindowsFontDatabase::systemDefaultFont().family()); + EnumFontFamiliesEx(dummy, &lf, populateFontFamilies, reinterpret_cast<LPARAM>(&context), 0); + ReleaseDC(0, dummy); + // Work around EnumFontFamiliesEx() not listing the system font. + if (!context.seenSystemDefaultFont) + QPlatformFontDatabase::registerFontFamily(context.systemDefaultFont); +} + +typedef QSharedPointer<QWindowsFontEngineData> QWindowsFontEngineDataPtr; + +#ifndef QT_NO_THREAD +typedef QThreadStorage<QWindowsFontEngineDataPtr> FontEngineThreadLocalData; + +Q_GLOBAL_STATIC(FontEngineThreadLocalData, fontEngineThreadLocalData) + +QSharedPointer<QWindowsFontEngineData> sharedFontData() +{ + FontEngineThreadLocalData *data = fontEngineThreadLocalData(); + if (!data->hasLocalData()) + data->setLocalData(QSharedPointer<QWindowsFontEngineData>::create()); + return data->localData(); +} +#else // !QT_NO_THREAD +Q_GLOBAL_STATIC(QWindowsFontEngineDataPtr, fontEngineData) + +QWindowsFontEngineDataPtr sharedFontData() +{ + QWindowsFontEngineDataPtr *data = fontEngineData(); + if (data->isNull()) + *data = QWindowsFontEngineDataPtr::create(); + return *data; +} +#endif // QT_NO_THREAD + +extern Q_GUI_EXPORT bool qt_needs_a8_gamma_correction; + +QWindowsFontDatabase::QWindowsFontDatabase() +{ + // Properties accessed by QWin32PrintEngine (Qt Print Support) + static const int hfontMetaTypeId = qRegisterMetaType<HFONT>(); + static const int logFontMetaTypeId = qRegisterMetaType<LOGFONT>(); + Q_UNUSED(hfontMetaTypeId) + Q_UNUSED(logFontMetaTypeId) + + if (lcQpaFonts().isDebugEnabled()) { + const QWindowsFontEngineDataPtr data = sharedFontData(); + qCDebug(lcQpaFonts) << __FUNCTION__ << "Clear type: " + << data->clearTypeEnabled << "gamma: " << data->fontSmoothingGamma; + } + qt_needs_a8_gamma_correction = true; +} + +QWindowsFontDatabase::~QWindowsFontDatabase() +{ + removeApplicationFonts(); +} + +QFontEngineMulti *QWindowsFontDatabase::fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) +{ + return new QWindowsMultiFontEngine(fontEngine, script); +} + +QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, void *handle) +{ + QFontEngine *fe = QWindowsFontDatabase::createEngine(fontDef, + defaultVerticalDPI(), + sharedFontData()); + qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef << fe << handle; + return fe; +} + +QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +{ + EmbeddedFont font(fontData); + QFontEngine *fontEngine = 0; + +#if !defined(QT_NO_DIRECTWRITE) + if (!useDirectWrite(hintingPreference)) +#endif + { + GUID guid; + CoCreateGuid(&guid); + +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wstrict-aliasing") + QString uniqueFamilyName = QLatin1Char('f') + + QString::number(guid.Data1, 36) + QLatin1Char('-') + + QString::number(guid.Data2, 36) + QLatin1Char('-') + + QString::number(guid.Data3, 36) + QLatin1Char('-') + + QString::number(*reinterpret_cast<quint64 *>(guid.Data4), 36); +QT_WARNING_POP + + QString actualFontName = font.changeFamilyName(uniqueFamilyName); + if (actualFontName.isEmpty()) { + qWarning("%s: Can't change family name of font", __FUNCTION__); + return 0; + } + + DWORD count = 0; + QByteArray newFontData = font.data(); + HANDLE fontHandle = + AddFontMemResourceEx(const_cast<char *>(newFontData.constData()), + DWORD(newFontData.size()), 0, &count); + if (count == 0 && fontHandle != 0) { + RemoveFontMemResourceEx(fontHandle); + fontHandle = 0; + } + + if (fontHandle == 0) { + qWarning("%s: AddFontMemResourceEx failed", __FUNCTION__); + } else { + QFontDef request; + request.family = uniqueFamilyName; + request.pixelSize = pixelSize; + request.styleStrategy = QFont::PreferMatch; + request.hintingPreference = hintingPreference; + request.stretch = QFont::Unstretched; + + fontEngine = QWindowsFontDatabase::createEngine(request, + defaultVerticalDPI(), + sharedFontData()); + + if (fontEngine) { + if (request.family != fontEngine->fontDef.family) { + qWarning("%s: Failed to load font. Got fallback instead: %s", + __FUNCTION__, qPrintable(fontEngine->fontDef.family)); + if (fontEngine->ref.load() == 0) + delete fontEngine; + fontEngine = 0; + } else { + Q_ASSERT(fontEngine->ref.load() == 0); + + // Override the generated font name + switch (fontEngine->type()) { + case QFontEngine::Win: + static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName); + fontEngine->fontDef.family = actualFontName; + break; + +#if !defined(QT_NO_DIRECTWRITE) + case QFontEngine::DirectWrite: + static_cast<QWindowsFontEngineDirectWrite *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName); + fontEngine->fontDef.family = actualFontName; + break; +#endif // !QT_NO_DIRECTWRITE + + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled font engine."); + } + + UniqueFontData uniqueData; + uniqueData.handle = fontHandle; + uniqueData.refCount.ref(); + m_uniqueFontData[uniqueFamilyName] = uniqueData; + } + } else { + RemoveFontMemResourceEx(fontHandle); + } + } + } +#if !defined(QT_NO_DIRECTWRITE) + else { + CustomFontFileLoader fontFileLoader; + fontFileLoader.addKey(this, fontData); + + QSharedPointer<QWindowsFontEngineData> fontEngineData = sharedFontData(); + if (!initDirectWrite(fontEngineData.data())) + return 0; + + IDWriteFontFile *fontFile = 0; + void *key = this; + + HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key, + sizeof(void *), + fontFileLoader.loader(), + &fontFile); + if (FAILED(hres)) { + qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__); + return 0; + } + + BOOL isSupportedFontType; + DWRITE_FONT_FILE_TYPE fontFileType; + DWRITE_FONT_FACE_TYPE fontFaceType; + UINT32 numberOfFaces; + fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces); + if (!isSupportedFontType) { + fontFile->Release(); + return 0; + } + + IDWriteFontFace *directWriteFontFace = 0; + hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType, + 1, + &fontFile, + 0, + DWRITE_FONT_SIMULATIONS_NONE, + &directWriteFontFace); + if (FAILED(hres)) { + qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__); + fontFile->Release(); + return 0; + } + + fontFile->Release(); + + fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace, + pixelSize, + fontEngineData); + + // Get font family from font data + fontEngine->fontDef.family = font.familyName(); + fontEngine->fontDef.hintingPreference = hintingPreference; + + directWriteFontFace->Release(); + } +#endif + + // Get style and weight info + if (fontEngine != 0) { + TableDirectory *os2TableEntry = font.tableDirectoryEntry("OS/2"); + if (os2TableEntry != 0) { + const OS2Table *os2Table = + reinterpret_cast<const OS2Table *>(fontData.constData() + + qFromBigEndian<quint32>(os2TableEntry->offset)); + + bool italic = qFromBigEndian<quint16>(os2Table->selection) & 1; + bool oblique = qFromBigEndian<quint16>(os2Table->selection) & 128; + + if (italic) + fontEngine->fontDef.style = QFont::StyleItalic; + else if (oblique) + fontEngine->fontDef.style = QFont::StyleOblique; + else + fontEngine->fontDef.style = QFont::StyleNormal; + + fontEngine->fontDef.weight = QPlatformFontDatabase::weightFromInteger(qFromBigEndian<quint16>(os2Table->weightClass)); + } + } + + qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fontEngine; + return fontEngine; +} + +static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData) +{ + QList<quint32> offsets; + const quint32 headerTag = *reinterpret_cast<const quint32 *>(fontData); + if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) { + if (headerTag != MAKE_TAG(0, 1, 0, 0) + && headerTag != MAKE_TAG('O', 'T', 'T', 'O') + && headerTag != MAKE_TAG('t', 'r', 'u', 'e') + && headerTag != MAKE_TAG('t', 'y', 'p', '1')) + return offsets; + offsets << 0; + return offsets; + } + const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8); + for (uint i = 0; i < numFonts; ++i) { + offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4); + } + return offsets; +} + +static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length) +{ + const quint16 numTables = qFromBigEndian<quint16>(data + 4); + for (uint i = 0; i < numTables; ++i) { + const quint32 offset = 12 + 16 * i; + if (*reinterpret_cast<const quint32 *>(data + offset) == tag) { + *table = fileBegin + qFromBigEndian<quint32>(data + offset + 8); + *length = qFromBigEndian<quint32>(data + offset + 12); + return; + } + } + *table = 0; + *length = 0; + return; +} + +static void getFamiliesAndSignatures(const QByteArray &fontData, + QList<FontNames> *families, + QVector<FONTSIGNATURE> *signatures) +{ + const uchar *data = reinterpret_cast<const uchar *>(fontData.constData()); + + QList<quint32> offsets = getTrueTypeFontOffsets(data); + if (offsets.isEmpty()) + return; + + for (int i = 0; i < offsets.count(); ++i) { + const uchar *font = data + offsets.at(i); + const uchar *table; + quint32 length; + getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length); + if (!table) + continue; + FontNames names = getCanonicalFontNames(table, length); + if (names.name.isEmpty()) + continue; + + families->append(qMove(names)); + + if (signatures) { + FONTSIGNATURE signature; + getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length); + if (table && length >= 86) { + // Offsets taken from OS/2 table in the TrueType spec + signature.fsUsb[0] = qFromBigEndian<quint32>(table + 42); + signature.fsUsb[1] = qFromBigEndian<quint32>(table + 46); + signature.fsUsb[2] = qFromBigEndian<quint32>(table + 50); + signature.fsUsb[3] = qFromBigEndian<quint32>(table + 54); + + signature.fsCsb[0] = qFromBigEndian<quint32>(table + 78); + signature.fsCsb[1] = qFromBigEndian<quint32>(table + 82); + } else { + memset(&signature, 0, sizeof(signature)); + } + signatures->append(signature); + } + } +} + +QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) +{ + WinApplicationFont font; + font.fileName = fileName; + QVector<FONTSIGNATURE> signatures; + QList<FontNames> families; + QStringList familyNames; + + if (!fontData.isEmpty()) { + getFamiliesAndSignatures(fontData, &families, &signatures); + if (families.isEmpty()) + return familyNames; + + DWORD dummy = 0; + font.handle = + AddFontMemResourceEx(const_cast<char *>(fontData.constData()), + DWORD(fontData.size()), 0, &dummy); + if (font.handle == 0) + return QStringList(); + + // Memory fonts won't show up in enumeration, so do add them the hard way. + for (int j = 0; j < families.count(); ++j) { + const QString familyName = families.at(j).name; + const QString styleName = families.at(j).style; + familyNames << familyName; + HDC hdc = GetDC(0); + LOGFONT lf; + memset(&lf, 0, sizeof(LOGFONT)); + memcpy(lf.lfFaceName, familyName.utf16(), sizeof(wchar_t) * qMin(LF_FACESIZE - 1, familyName.size())); + lf.lfCharSet = DEFAULT_CHARSET; + HFONT hfont = CreateFontIndirect(&lf); + HGDIOBJ oldobj = SelectObject(hdc, hfont); + + TEXTMETRIC textMetrics; + GetTextMetrics(hdc, &textMetrics); + + addFontToDatabase(familyName, styleName, lf.lfCharSet, &textMetrics, &signatures.at(j), + TRUETYPE_FONTTYPE, true); + + SelectObject(hdc, oldobj); + DeleteObject(hfont); + ReleaseDC(0, hdc); + } + } else { + QFile f(fileName); + if (!f.open(QIODevice::ReadOnly)) + return QStringList(); + QByteArray data = f.readAll(); + f.close(); + + getFamiliesAndSignatures(data, &families, 0); + if (families.isEmpty()) + return QStringList(); + + if (AddFontResourceExW((wchar_t*)fileName.utf16(), FR_PRIVATE, 0) == 0) + return QStringList(); + + font.handle = 0; + + // Fonts based on files are added via populate, as they will show up in font enumeration. + for (int j = 0; j < families.count(); ++j) { + const QString familyName = families.at(j).name; + familyNames << familyName; + populateFamily(familyName, true); + } + } + + m_applicationFonts << font; + + return familyNames; +} + +void QWindowsFontDatabase::removeApplicationFonts() +{ + foreach (const WinApplicationFont &font, m_applicationFonts) { + if (font.handle) { + RemoveFontMemResourceEx(font.handle); + } else { + RemoveFontResourceExW((LPCWSTR)font.fileName.utf16(), FR_PRIVATE, 0); + } + } + m_applicationFonts.clear(); +} + +void QWindowsFontDatabase::releaseHandle(void * /* handle */) +{ +} + +QString QWindowsFontDatabase::fontDir() const +{ + const QString result = QPlatformFontDatabase::fontDir(); + qCDebug(lcQpaFonts) << __FUNCTION__ << result; + return result; +} + +bool QWindowsFontDatabase::fontsAlwaysScalable() const +{ + return true; +} + +void QWindowsFontDatabase::derefUniqueFont(const QString &uniqueFont) +{ + if (m_uniqueFontData.contains(uniqueFont)) { + if (!m_uniqueFontData[uniqueFont].refCount.deref()) { + RemoveFontMemResourceEx(m_uniqueFontData[uniqueFont].handle); + m_uniqueFontData.remove(uniqueFont); + } + } +} + +void QWindowsFontDatabase::refUniqueFont(const QString &uniqueFont) +{ + if (m_uniqueFontData.contains(uniqueFont)) + m_uniqueFontData[uniqueFont].refCount.ref(); +} + +HFONT QWindowsFontDatabase::systemFont() +{ + static const HFONT stock_sysfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); + return stock_sysfont; +} + +// Creation functions + +static const char *other_tryFonts[] = { + "Arial", + "MS UI Gothic", + "Gulim", + "SimSun", + "PMingLiU", + "Arial Unicode MS", + 0 +}; + +static const char *jp_tryFonts [] = { + "MS UI Gothic", + "Arial", + "Gulim", + "SimSun", + "PMingLiU", + "Arial Unicode MS", + 0 +}; + +static const char *ch_CN_tryFonts [] = { + "SimSun", + "Arial", + "PMingLiU", + "Gulim", + "MS UI Gothic", + "Arial Unicode MS", + 0 +}; + +static const char *ch_TW_tryFonts [] = { + "PMingLiU", + "Arial", + "SimSun", + "Gulim", + "MS UI Gothic", + "Arial Unicode MS", + 0 +}; + +static const char *kr_tryFonts[] = { + "Gulim", + "Arial", + "PMingLiU", + "SimSun", + "MS UI Gothic", + "Arial Unicode MS", + 0 +}; + +static const char **tryFonts = 0; + +LOGFONT QWindowsFontDatabase::fontDefToLOGFONT(const QFontDef &request) +{ + LOGFONT lf; + memset(&lf, 0, sizeof(LOGFONT)); + + lf.lfHeight = -qRound(request.pixelSize); + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + if (request.weight == 50) + lf.lfWeight = FW_DONTCARE; + else + lf.lfWeight = (request.weight*900)/99; + lf.lfItalic = request.style != QFont::StyleNormal; + lf.lfCharSet = DEFAULT_CHARSET; + + int strat = OUT_DEFAULT_PRECIS; + if (request.styleStrategy & QFont::PreferBitmap) { + strat = OUT_RASTER_PRECIS; + } else if (request.styleStrategy & QFont::PreferDevice) { + strat = OUT_DEVICE_PRECIS; + } else if (request.styleStrategy & QFont::PreferOutline) { + strat = OUT_OUTLINE_PRECIS; + } else if (request.styleStrategy & QFont::ForceOutline) { + strat = OUT_TT_ONLY_PRECIS; + } + + lf.lfOutPrecision = strat; + + int qual = DEFAULT_QUALITY; + + if (request.styleStrategy & QFont::PreferMatch) + qual = DRAFT_QUALITY; + else if (request.styleStrategy & QFont::PreferQuality) + qual = PROOF_QUALITY; + + if (request.styleStrategy & QFont::PreferAntialias) { + if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && !(request.styleStrategy & QFont::NoSubpixelAntialias)) { + qual = CLEARTYPE_QUALITY; + } else { + qual = ANTIALIASED_QUALITY; + } + } else if (request.styleStrategy & QFont::NoAntialias) { + qual = NONANTIALIASED_QUALITY; + } else if ((request.styleStrategy & QFont::NoSubpixelAntialias) && sharedFontData()->clearTypeEnabled) { + qual = ANTIALIASED_QUALITY; + } + + lf.lfQuality = qual; + + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + + int hint = FF_DONTCARE; + switch (request.styleHint) { + case QFont::Helvetica: + hint = FF_SWISS; + break; + case QFont::Times: + hint = FF_ROMAN; + break; + case QFont::Courier: + hint = FF_MODERN; + break; + case QFont::OldEnglish: + hint = FF_DECORATIVE; + break; + case QFont::System: + hint = FF_MODERN; + break; + default: + break; + } + + lf.lfPitchAndFamily = DEFAULT_PITCH | hint; + + QString fam = request.family; + if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) { + qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam)); + fam.truncate(LF_FACESIZE - 1); + } + + if (fam.isEmpty()) + fam = QStringLiteral("MS Sans Serif"); + + if (fam == QLatin1String("MS Sans Serif") + && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) { + fam = QStringLiteral("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale + } + if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap)) + fam = QStringLiteral("Courier New"); + + memcpy(lf.lfFaceName, fam.utf16(), fam.size() * sizeof(wchar_t)); + + return lf; +} + +QStringList QWindowsFontDatabase::extraTryFontsForFamily(const QString &family) +{ + QStringList result; + QFontDatabase db; + if (!db.writingSystems(family).contains(QFontDatabase::Symbol)) { + if (!tryFonts) { + LANGID lid = GetUserDefaultLangID(); + switch (lid&0xff) { + case LANG_CHINESE: // Chinese + if ( lid == 0x0804 || lid == 0x1004) // China mainland and Singapore + tryFonts = ch_CN_tryFonts; + else + tryFonts = ch_TW_tryFonts; // Taiwan, Hong Kong and Macau + break; + case LANG_JAPANESE: + tryFonts = jp_tryFonts; + break; + case LANG_KOREAN: + tryFonts = kr_tryFonts; + break; + default: + tryFonts = other_tryFonts; + break; + } + } + QFontDatabase db; + const QStringList families = db.families(); + const char **tf = tryFonts; + while (tf && *tf) { + // QTBUG-31689, family might be an English alias for a localized font name. + const QString family = QString::fromLatin1(*tf); + if (families.contains(family) || db.hasFamily(family)) + result << family; + ++tf; + } + } + result.append(QStringLiteral("Segoe UI Emoji")); + result.append(QStringLiteral("Segoe UI Symbol")); + return result; +} + +QString QWindowsFontDatabase::familyForStyleHint(QFont::StyleHint styleHint) +{ + switch (styleHint) { + case QFont::Times: + return QStringLiteral("Times New Roman"); + case QFont::Courier: + return QStringLiteral("Courier New"); + case QFont::Monospace: + return QStringLiteral("Courier New"); + case QFont::Cursive: + return QStringLiteral("Comic Sans MS"); + case QFont::Fantasy: + return QStringLiteral("Impact"); + case QFont::Decorative: + return QStringLiteral("Old English"); + case QFont::Helvetica: + return QStringLiteral("Arial"); + case QFont::System: + default: + break; + } + return QStringLiteral("MS Shell Dlg 2"); +} + +QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const +{ + QStringList result; + result.append(QWindowsFontDatabase::familyForStyleHint(styleHint)); + result.append(QWindowsFontDatabase::extraTryFontsForFamily(family)); + result.append(QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + + qCDebug(lcQpaFonts) << __FUNCTION__ << family << style << styleHint + << script << result; + return result; +} + + +QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, + int dpi, + const QSharedPointer<QWindowsFontEngineData> &data) +{ + QFontEngine *fe = 0; + + LOGFONT lf = fontDefToLOGFONT(request); + const bool preferClearTypeAA = lf.lfQuality == CLEARTYPE_QUALITY; + + if (request.stretch != 100) { + HFONT hfont = CreateFontIndirect(&lf); + if (!hfont) { + qErrnoWarning("%s: CreateFontIndirect failed", __FUNCTION__); + hfont = QWindowsFontDatabase::systemFont(); + } + + HGDIOBJ oldObj = SelectObject(data->hdc, hfont); + TEXTMETRIC tm; + if (!GetTextMetrics(data->hdc, &tm)) + qErrnoWarning("%s: GetTextMetrics failed", __FUNCTION__); + else + lf.lfWidth = tm.tmAveCharWidth * request.stretch / 100; + SelectObject(data->hdc, oldObj); + + DeleteObject(hfont); + } + +#if !defined(QT_NO_DIRECTWRITE) + if (initDirectWrite(data.data())) { + const QString fam = QString::fromWCharArray(lf.lfFaceName); + const QString nameSubstitute = QWindowsFontEngineDirectWrite::fontNameSubstitute(fam); + if (nameSubstitute != fam) { + const int nameSubstituteLength = qMin(nameSubstitute.length(), LF_FACESIZE - 1); + memcpy(lf.lfFaceName, nameSubstitute.utf16(), nameSubstituteLength * sizeof(wchar_t)); + lf.lfFaceName[nameSubstituteLength] = 0; + } + + HFONT hfont = CreateFontIndirect(&lf); + if (!hfont) { + qErrnoWarning("%s: CreateFontIndirect failed", __FUNCTION__); + } else { + HGDIOBJ oldFont = SelectObject(data->hdc, hfont); + + IDWriteFontFace *directWriteFontFace = NULL; + HRESULT hr = data->directWriteGdiInterop->CreateFontFaceFromHdc(data->hdc, &directWriteFontFace); + if (FAILED(hr)) { + const QString errorString = qt_error_string(int(hr)); + qWarning().noquote().nospace() << "DirectWrite: CreateFontFaceFromHDC() failed (" + << errorString << ") for " << request << ' ' << lf << " dpi=" << dpi; + } else { + bool isColorFont = false; +#if defined(QT_USE_DIRECTWRITE2) + IDWriteFontFace2 *directWriteFontFace2 = Q_NULLPTR; + if (SUCCEEDED(directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2), + reinterpret_cast<void **>(&directWriteFontFace2)))) { + if (directWriteFontFace2->IsColorFont()) + isColorFont = directWriteFontFace2->GetPaletteEntryCount() > 0; + } +#endif + const QFont::HintingPreference hintingPreference = + static_cast<QFont::HintingPreference>(request.hintingPreference); + const bool useDw = useDirectWrite(hintingPreference, fam, isColorFont); + qCDebug(lcQpaFonts) << __FUNCTION__ << request.family << request.pointSize + << "pt" << "hintingPreference=" << hintingPreference << "color=" << isColorFont + << dpi << "dpi" << "useDirectWrite=" << useDw; + if (useDw) { + QWindowsFontEngineDirectWrite *fedw = new QWindowsFontEngineDirectWrite(directWriteFontFace, + request.pixelSize, + data); + + wchar_t n[64]; + GetTextFace(data->hdc, 64, n); + + QFontDef fontDef = request; + fontDef.family = QString::fromWCharArray(n); + + if (isColorFont) + fedw->glyphFormat = QFontEngine::Format_ARGB; + fedw->initFontInfo(fontDef, dpi); + fe = fedw; + } else { + directWriteFontFace->Release(); + } + } + + SelectObject(data->hdc, oldFont); + DeleteObject(hfont); + } + } +#endif // QT_NO_DIRECTWRITE + + if (!fe) { + QWindowsFontEngine *few = new QWindowsFontEngine(request.family, lf, data); + if (preferClearTypeAA) + few->glyphFormat = QFontEngine::Format_A32; + few->initFontInfo(request, dpi); + fe = few; + } + + return fe; +} + +QFont QWindowsFontDatabase::systemDefaultFont() +{ + LOGFONT lf; + GetObject(QWindowsFontDatabase::systemFont(), sizeof(lf), &lf); + QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(lf); + // "MS Shell Dlg 2" is the correct system font >= Win2k + if (systemFont.family() == QLatin1String("MS Shell Dlg")) + systemFont.setFamily(QStringLiteral("MS Shell Dlg 2")); + qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont; + return systemFont; +} + +QFont QWindowsFontDatabase::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In) +{ + if (verticalDPI_In <= 0) + verticalDPI_In = defaultVerticalDPI(); + QFont qFont(QString::fromWCharArray(logFont.lfFaceName)); + qFont.setItalic(logFont.lfItalic); + if (logFont.lfWeight != FW_DONTCARE) + qFont.setWeight(QPlatformFontDatabase::weightFromInteger(logFont.lfWeight)); + const qreal logFontHeight = qAbs(logFont.lfHeight); + qFont.setPointSizeF(logFontHeight * 72.0 / qreal(verticalDPI_In)); + qFont.setUnderline(logFont.lfUnderline); + qFont.setOverline(false); + qFont.setStrikeOut(logFont.lfStrikeOut); + return qFont; +} + +int QWindowsFontDatabase::defaultVerticalDPI() +{ + static int vDPI = -1; + if (vDPI == -1) { + if (HDC defaultDC = GetDC(0)) { + vDPI = GetDeviceCaps(defaultDC, LOGPIXELSY); + ReleaseDC(0, defaultDC); + } else { + // FIXME: Resolve now or return 96 and keep unresolved? + vDPI = 96; + } + } + return vDPI; +} + +QString QWindowsFontDatabase::readRegistryString(HKEY parentHandle, const wchar_t *keyPath, const wchar_t *keyName) +{ + QString result; + HKEY handle = 0; + if (RegOpenKeyEx(parentHandle, keyPath, 0, KEY_READ, &handle) == ERROR_SUCCESS) { + // get the size and type of the value + DWORD dataType; + DWORD dataSize; + if (RegQueryValueEx(handle, keyName, 0, &dataType, 0, &dataSize) == ERROR_SUCCESS) { + if (dataType == REG_SZ || dataType == REG_EXPAND_SZ) { + dataSize += 2; // '\0' missing? + QVarLengthArray<unsigned char> data(dataSize); + data[dataSize - 2] = data[dataSize - 1] = '\0'; + if (RegQueryValueEx(handle, keyName, 0, 0, data.data(), &dataSize) == ERROR_SUCCESS) + result = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.data())); + } + } + RegCloseKey(handle); + } + return result; +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp new file mode 100644 index 0000000000..4d973bbf17 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** 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 "qwindowsfontdatabase_ft_p.h" +#include "qwindowsfontdatabase_p.h" + +#include <ft2build.h> +#include FT_TRUETYPE_TABLES_H + +#include <QtCore/QDir> +#include <QtCore/QDirIterator> +#include <QtCore/QSettings> +#include <QtCore/QRegularExpression> +#include <QtGui/private/qfontengine_ft_p.h> +#include <QtGui/QGuiApplication> +#include <QtGui/QFontDatabase> + +#include <wchar.h> + +QT_BEGIN_NAMESPACE + +static inline QFontDatabase::WritingSystem writingSystemFromCharSet(uchar charSet) +{ + switch (charSet) { + case ANSI_CHARSET: + case EASTEUROPE_CHARSET: + case BALTIC_CHARSET: + case TURKISH_CHARSET: + return QFontDatabase::Latin; + case GREEK_CHARSET: + return QFontDatabase::Greek; + case RUSSIAN_CHARSET: + return QFontDatabase::Cyrillic; + case HEBREW_CHARSET: + return QFontDatabase::Hebrew; + case ARABIC_CHARSET: + return QFontDatabase::Arabic; + case THAI_CHARSET: + return QFontDatabase::Thai; + case GB2312_CHARSET: + return QFontDatabase::SimplifiedChinese; + case CHINESEBIG5_CHARSET: + return QFontDatabase::TraditionalChinese; + case SHIFTJIS_CHARSET: + return QFontDatabase::Japanese; + case HANGUL_CHARSET: + case JOHAB_CHARSET: + return QFontDatabase::Korean; + case VIETNAMESE_CHARSET: + return QFontDatabase::Vietnamese; + case SYMBOL_CHARSET: + return QFontDatabase::Symbol; + default: + break; + } + return QFontDatabase::Any; +} + +static FontFile * createFontFile(const QString &fileName, int index) +{ + FontFile *fontFile = new FontFile; + fontFile->fileName = fileName; + fontFile->indexValue = index; + return fontFile; +} + +extern bool localizedName(const QString &name); +extern QString getEnglishName(const QString &familyName, bool includeStyle = false); + +namespace { +struct FontKey +{ + QString fileName; + QStringList fontNames; +}; +} // namespace + +typedef QVector<FontKey> FontKeys; + +static FontKeys &fontKeys() +{ + static FontKeys result; + if (result.isEmpty()) { + const QSettings fontRegistry(QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"), + QSettings::NativeFormat); + const QStringList allKeys = fontRegistry.allKeys(); + const QString trueType = QStringLiteral("(TrueType)"); +#ifndef QT_NO_REGULAREXPRESSION + const QRegularExpression sizeListMatch(QStringLiteral("\\s(\\d+,)+\\d+")); +#else + const QRegExp sizeListMatch(QLatin1String("\\s(\\d+,)+\\d+")); +#endif + Q_ASSERT(sizeListMatch.isValid()); + const int size = allKeys.size(); + result.reserve(size); + for (int i = 0; i < size; ++i) { + FontKey fontKey; + const QString ®istryFontKey = allKeys.at(i); + fontKey.fileName = fontRegistry.value(registryFontKey).toString(); + QString realKey = registryFontKey; + realKey.remove(trueType); + realKey.remove(sizeListMatch); + const auto fontNames = QStringRef(&realKey).trimmed().split(QLatin1Char('&')); + fontKey.fontNames.reserve(fontNames.size()); + for (const QStringRef &fontName : fontNames) + fontKey.fontNames.append(fontName.trimmed().toString()); + result.append(fontKey); + } + } + return result; +} + +static const FontKey *findFontKey(const QString &name, int *indexIn = Q_NULLPTR) +{ + const FontKeys &keys = fontKeys(); + for (auto it = keys.constBegin(), cend = keys.constEnd(); it != cend; ++it) { + const int index = it->fontNames.indexOf(name); + if (index >= 0) { + if (indexIn) + *indexIn = index; + return &(*it); + } + } + if (indexIn) + *indexIn = -1; + return Q_NULLPTR; +} + +static bool addFontToDatabase(const QString &faceName, + const QString &styleName, + const QString &fullName, + uchar charSet, + const TEXTMETRIC *textmetric, + const FONTSIGNATURE *signature, + int type, + bool registerAlias) +{ + // the "@family" fonts are just the same as "family". Ignore them. + if (faceName.isEmpty() || faceName.at(0) == QLatin1Char('@') || faceName.startsWith(QLatin1String("WST_"))) + return false; + + static const int SMOOTH_SCALABLE = 0xffff; + const QString foundryName; // No such concept. + const bool fixed = !(textmetric->tmPitchAndFamily & TMPF_FIXED_PITCH); + const bool ttf = (textmetric->tmPitchAndFamily & TMPF_TRUETYPE); + const bool scalable = textmetric->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE); + const int size = scalable ? SMOOTH_SCALABLE : textmetric->tmHeight; + const QFont::Style style = textmetric->tmItalic ? QFont::StyleItalic : QFont::StyleNormal; + const bool antialias = false; + const QFont::Weight weight = QPlatformFontDatabase::weightFromInteger(textmetric->tmWeight); + const QFont::Stretch stretch = QFont::Unstretched; + +#ifndef QT_NO_DEBUG_STREAM + if (lcQpaFonts().isDebugEnabled()) { + QString message; + QTextStream str(&message); + str << __FUNCTION__ << ' ' << faceName << "::" << fullName << ' ' << charSet << " TTF=" << ttf; + if (type & DEVICE_FONTTYPE) + str << " DEVICE"; + if (type & RASTER_FONTTYPE) + str << " RASTER"; + if (type & TRUETYPE_FONTTYPE) + str << " TRUETYPE"; + str << " scalable=" << scalable << " Size=" << size + << " Style=" << style << " Weight=" << weight + << " stretch=" << stretch; + qCDebug(lcQpaFonts) << message; + } +#endif + + QString englishName; + if (registerAlias & ttf && localizedName(faceName)) + englishName = getEnglishName(faceName); + + QSupportedWritingSystems writingSystems; + if (type & TRUETYPE_FONTTYPE) { + Q_ASSERT(signature); + quint32 unicodeRange[4] = { + signature->fsUsb[0], signature->fsUsb[1], + signature->fsUsb[2], signature->fsUsb[3] + }; + quint32 codePageRange[2] = { + signature->fsCsb[0], signature->fsCsb[1] + }; + writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + // ### Hack to work around problem with Thai text on Windows 7. Segoe UI contains + // the symbol for Baht, and Windows thus reports that it supports the Thai script. + // Since it's the default UI font on this platform, most widgets will be unable to + // display Thai text by default. As a temporary work around, we special case Segoe UI + // and remove the Thai script from its list of supported writing systems. + if (writingSystems.supported(QFontDatabase::Thai) && + faceName == QLatin1String("Segoe UI")) + writingSystems.setSupported(QFontDatabase::Thai, false); + } else { + const QFontDatabase::WritingSystem ws = writingSystemFromCharSet(charSet); + if (ws != QFontDatabase::Any) + writingSystems.setSupported(ws); + } + + int index = 0; + const FontKey *key = findFontKey(fullName, &index); + if (!key) { + // On non-English locales, the styles of the font may be localized in enumeration, but + // not in the registry. + QLocale systemLocale = QLocale::system(); + if (systemLocale.language() != QLocale::C + && systemLocale.language() != QLocale::English + && styleName != QLatin1String("Italic") + && styleName != QLatin1String("Bold")) { + key = findFontKey(getEnglishName(fullName, true), &index); + } + if (!key) + key = findFontKey(faceName, &index); + if (!key && !registerAlias && englishName.isEmpty() && localizedName(faceName)) + englishName = getEnglishName(faceName); + if (!key && !englishName.isEmpty()) + key = findFontKey(englishName, &index); + if (!key) + return false; + } + QString value = key->fileName; + if (value.isEmpty()) + return false; + + if (!QDir::isAbsolutePath(value)) + value.prepend(QFile::decodeName(qgetenv("windir") + "\\Fonts\\")); + + QPlatformFontDatabase::registerFont(faceName, styleName, foundryName, weight, style, stretch, + antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); + + // add fonts windows can generate for us: + if (weight <= QFont::DemiBold && styleName.isEmpty()) + QPlatformFontDatabase::registerFont(faceName, QString(), foundryName, QFont::Bold, style, stretch, + antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); + + if (style != QFont::StyleItalic && styleName.isEmpty()) + QPlatformFontDatabase::registerFont(faceName, QString(), foundryName, weight, QFont::StyleItalic, stretch, + antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); + + if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty()) + QPlatformFontDatabase::registerFont(faceName, QString(), foundryName, QFont::Bold, QFont::StyleItalic, stretch, + antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); + + if (!englishName.isEmpty()) + QPlatformFontDatabase::registerAliasToFontFamily(faceName, englishName); + + return true; +} + +static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *textmetric, + DWORD type, LPARAM) +{ + const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont); + const QString faceName = QString::fromWCharArray(f->elfLogFont.lfFaceName); + const QString styleName = QString::fromWCharArray(f->elfStyle); + const QString fullName = QString::fromWCharArray(f->elfFullName); + const uchar charSet = f->elfLogFont.lfCharSet; + + // NEWTEXTMETRICEX (passed for TT fonts) is a NEWTEXTMETRIC, which according + // to the documentation is identical to a TEXTMETRIC except for the last four + // members, which we don't use anyway + const FONTSIGNATURE *signature = Q_NULLPTR; + if (type & TRUETYPE_FONTTYPE) + signature = &reinterpret_cast<const NEWTEXTMETRICEX *>(textmetric)->ntmFontSig; + addFontToDatabase(faceName, styleName, fullName, charSet, textmetric, signature, type, false); + + // keep on enumerating + return 1; +} + +/*! + \brief Populate font database using EnumFontFamiliesEx(). + + Normally, leaving the name empty should enumerate + all fonts, however, system fonts like "MS Shell Dlg 2" + are only found when specifying the name explicitly. +*/ + +void QWindowsFontDatabaseFT::populateFamily(const QString &familyName) +{ + qCDebug(lcQpaFonts) << familyName; + if (familyName.size() >= LF_FACESIZE) { + qCWarning(lcQpaFonts) << "Unable to enumerate family '" << familyName << '\''; + return; + } + HDC dummy = GetDC(0); + LOGFONT lf; + lf.lfCharSet = DEFAULT_CHARSET; + familyName.toWCharArray(lf.lfFaceName); + lf.lfFaceName[familyName.size()] = 0; + lf.lfPitchAndFamily = 0; + EnumFontFamiliesEx(dummy, &lf, storeFont, 0, 0); + ReleaseDC(0, dummy); +} + +namespace { +// Context for enumerating system fonts, records whether the default font has been +// encountered, which is normally not enumerated. +struct PopulateFamiliesContext +{ + PopulateFamiliesContext(const QString &f) : systemDefaultFont(f), seenSystemDefaultFont(false) {} + + QString systemDefaultFont; + bool seenSystemDefaultFont; +}; +} // namespace + +// Delayed population of font families + +static int QT_WIN_CALLBACK populateFontFamilies(const LOGFONT *logFont, const TEXTMETRIC *textmetric, + DWORD, LPARAM lparam) +{ + const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont); + // the "@family" fonts are just the same as "family". Ignore them. + const wchar_t *faceNameW = f->elfLogFont.lfFaceName; + if (faceNameW[0] && faceNameW[0] != L'@' && wcsncmp(faceNameW, L"WST_", 4)) { + // Register only font families for which a font file exists for delayed population + const bool ttf = textmetric->tmPitchAndFamily & TMPF_TRUETYPE; + const QString faceName = QString::fromWCharArray(faceNameW); + const FontKey *key = findFontKey(faceName); + if (!key) { + key = findFontKey(QString::fromWCharArray(f->elfFullName)); + if (!key && ttf && localizedName(faceName)) + key = findFontKey(getEnglishName(faceName)); + } + if (key) { + QPlatformFontDatabase::registerFontFamily(faceName); + PopulateFamiliesContext *context = reinterpret_cast<PopulateFamiliesContext *>(lparam); + if (!context->seenSystemDefaultFont && faceName == context->systemDefaultFont) + context->seenSystemDefaultFont = true; + + // Register current font's english name as alias + if (ttf && localizedName(faceName)) { + const QString englishName = getEnglishName(faceName); + if (!englishName.isEmpty()) { + QPlatformFontDatabase::registerAliasToFontFamily(faceName, englishName); + // Check whether the system default font name is an alias of the current font family name, + // as on Chinese Windows, where the system font "SimSun" is an alias to a font registered under a local name + if (!context->seenSystemDefaultFont && englishName == context->systemDefaultFont) + context->seenSystemDefaultFont = true; + } + } + } + } + return 1; // continue +} + +void QWindowsFontDatabaseFT::populateFontDatabase() +{ + HDC dummy = GetDC(0); + LOGFONT lf; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfFaceName[0] = 0; + lf.lfPitchAndFamily = 0; + PopulateFamiliesContext context(QWindowsFontDatabase::systemDefaultFont().family()); + EnumFontFamiliesEx(dummy, &lf, populateFontFamilies, reinterpret_cast<LPARAM>(&context), 0); + ReleaseDC(0, dummy); + // Work around EnumFontFamiliesEx() not listing the system font + if (!context.seenSystemDefaultFont) + QPlatformFontDatabase::registerFontFamily(context.systemDefaultFont); +} + +QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, void *handle) +{ + QFontEngine *fe = QBasicFontDatabase::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); + qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fe; + return fe; +} + +QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const +{ + QStringList result; + result.append(QWindowsFontDatabase::familyForStyleHint(styleHint)); + result.append(QWindowsFontDatabase::extraTryFontsForFamily(family)); + result.append(QBasicFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + + qCDebug(lcQpaFonts) << __FUNCTION__ << family << style << styleHint + << script << result; + + return result; +} +QString QWindowsFontDatabaseFT::fontDir() const +{ + const QString result = QLatin1String(qgetenv("windir")) + QLatin1String("/Fonts");//QPlatformFontDatabase::fontDir(); + qCDebug(lcQpaFonts) << __FUNCTION__ << result; + return result; +} + +QFont QWindowsFontDatabaseFT::defaultFont() const +{ + return QWindowsFontDatabase::systemDefaultFont(); +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft_p.h b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft_p.h new file mode 100644 index 0000000000..3a432842e5 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_ft_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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 QWINDOWSFONTDATABASEFT_H +#define QWINDOWSFONTDATABASEFT_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 <QtFontDatabaseSupport/private/qbasicfontdatabase_p.h> +#include <QtCore/QSharedPointer> +#include <QtCore/qt_windows.h> + +QT_BEGIN_NAMESPACE + +class QWindowsFontDatabaseFT : public QBasicFontDatabase +{ +public: + void populateFontDatabase() Q_DECL_OVERRIDE; + void populateFamily(const QString &familyName) Q_DECL_OVERRIDE; + QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE; + QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, + QFont::HintingPreference hintingPreference) Q_DECL_OVERRIDE; + + QStringList fallbacksForFamily(const QString &family, QFont::Style style, + QFont::StyleHint styleHint, + QChar::Script script) const Q_DECL_OVERRIDE; + + QString fontDir() const Q_DECL_OVERRIDE; + QFont defaultFont() const Q_DECL_OVERRIDE; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSFONTDATABASEFT_H diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h new file mode 100644 index 0000000000..b7ebfc033f --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase_p.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** 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 QWINDOWSFONTDATABASE_H +#define QWINDOWSFONTDATABASE_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 <qpa/qplatformfontdatabase.h> +#include <QtCore/QSharedPointer> +#include <QtCore/QLoggingCategory> +#include <QtCore/qt_windows.h> + +#if !defined(QT_NO_DIRECTWRITE) + struct IDWriteFactory; + struct IDWriteGdiInterop; +#endif + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQpaFonts) + +class QWindowsFontEngineData +{ + Q_DISABLE_COPY(QWindowsFontEngineData) +public: + QWindowsFontEngineData(); + ~QWindowsFontEngineData(); + + uint pow_gamma[256]; + + bool clearTypeEnabled; + qreal fontSmoothingGamma; + HDC hdc; +#if !defined(QT_NO_DIRECTWRITE) + IDWriteFactory *directWriteFactory; + IDWriteGdiInterop *directWriteGdiInterop; +#endif +}; + +class QWindowsFontDatabase : public QPlatformFontDatabase +{ +public: + enum FontOptions { + // Relevant bits from QWindowsIntegration::Options + DontUseDirectWriteFonts = 0x40, + DontUseColorFonts = 0x80 + }; + + QWindowsFontDatabase(); + ~QWindowsFontDatabase(); + + void populateFontDatabase() Q_DECL_OVERRIDE; + void populateFamily(const QString &familyName) Q_DECL_OVERRIDE; + QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) Q_DECL_OVERRIDE; + QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE; + QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) Q_DECL_OVERRIDE; + QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const Q_DECL_OVERRIDE; + QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName) Q_DECL_OVERRIDE; + void releaseHandle(void *handle) Q_DECL_OVERRIDE; + QString fontDir() const Q_DECL_OVERRIDE; + + QFont defaultFont() const Q_DECL_OVERRIDE { return systemDefaultFont(); } + bool fontsAlwaysScalable() const Q_DECL_OVERRIDE; + void derefUniqueFont(const QString &uniqueFont); + void refUniqueFont(const QString &uniqueFont); + + static QFont systemDefaultFont(); + + static QFontEngine *createEngine(const QFontDef &request, + int dpi, + const QSharedPointer<QWindowsFontEngineData> &data); + + static HFONT systemFont(); + static QFont LOGFONT_to_QFont(const LOGFONT& lf, int verticalDPI = 0); + + static qreal fontSmoothingGamma(); + static LOGFONT fontDefToLOGFONT(const QFontDef &fontDef); + + static QStringList extraTryFontsForFamily(const QString &family); + static QString familyForStyleHint(QFont::StyleHint styleHint); + + static int defaultVerticalDPI(); + + static void setFontOptions(unsigned options); + static unsigned fontOptions(); + + static QString readRegistryString(HKEY parentHandle, const wchar_t *keyPath, const wchar_t *keyName); + +private: + void populateFamily(const QString &familyName, bool registerAlias); + void removeApplicationFonts(); + + struct WinApplicationFont { + HANDLE handle; + QString fileName; + }; + + QList<WinApplicationFont> m_applicationFonts; + + struct UniqueFontData { + HANDLE handle; + QAtomicInt refCount; + }; + + QMap<QString, UniqueFontData> m_uniqueFontData; + + static unsigned m_fontOptions; +}; + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug, const QFontDef &def); +#endif + +QT_END_NAMESPACE + +#endif // QWINDOWSFONTDATABASE_H diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp new file mode 100644 index 0000000000..9fc6fec915 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontengine.cpp @@ -0,0 +1,1330 @@ +/**************************************************************************** +** +** 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 "qwindowsfontengine_p.h" +#include "qwindowsnativeimage_p.h" +#include "qwindowsfontdatabase_p.h" +#include <QtCore/qt_windows.h> +#include "qwindowsfontenginedirectwrite_p.h" + +#include <QtGui/qpa/qplatformintegration.h> +#include <QtGui/private/qtextengine_p.h> // glyph_metrics_t +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/QPaintDevice> +#include <QtGui/QBitmap> +#include <QtGui/QPainter> +#include <QtGui/private/qpainter_p.h> +#include <QtGui/QPaintEngine> +#include <QtGui/private/qpaintengine_raster_p.h> + +#include <QtCore/QtEndian> +#include <QtCore/QFile> +#include <QtCore/qmath.h> +#include <QtCore/QThreadStorage> +#include <QtCore/private/qsystemlibrary_p.h> +#include <QtCore/private/qstringiterator_p.h> + +#include <QtCore/QDebug> + +#include <limits.h> + +#if !defined(QT_NO_DIRECTWRITE) +# include <dwrite.h> +#endif + +QT_BEGIN_NAMESPACE + +//### mingw needed define +#ifndef TT_PRIM_CSPLINE +#define TT_PRIM_CSPLINE 3 +#endif + +// GetFontData expects the tags in little endian ;( +#define MAKE_LITTLE_ENDIAN_TAG(ch1, ch2, ch3, ch4) (\ + (((quint32)(ch4)) << 24) | \ + (((quint32)(ch3)) << 16) | \ + (((quint32)(ch2)) << 8) | \ + ((quint32)(ch1)) \ + ) + +// common DC for all fonts + +typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT); +static PtrGetCharWidthI ptrGetCharWidthI = 0; +static bool resolvedGetCharWidthI = false; + +static void resolveGetCharWidthI() +{ + if (resolvedGetCharWidthI) + return; + resolvedGetCharWidthI = true; + ptrGetCharWidthI = (PtrGetCharWidthI)QSystemLibrary::resolve(QStringLiteral("gdi32"), "GetCharWidthI"); +} + +static inline quint16 getUShort(unsigned char *p) +{ + quint16 val; + val = *p++ << 8; + val |= *p; + + return val; +} + +// general font engine + +QFixed QWindowsFontEngine::lineThickness() const +{ + if(lineWidth > 0) + return lineWidth; + + return QFontEngine::lineThickness(); +} + +static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) +{ + int size; + size = GetOutlineTextMetrics(hdc, 0, 0); + OUTLINETEXTMETRIC *otm = (OUTLINETEXTMETRIC *)malloc(size); + GetOutlineTextMetrics(hdc, size, otm); + return otm; +} + +bool QWindowsFontEngine::hasCFFTable() const +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('C', 'F', 'F', ' '), 0, 0, 0) != GDI_ERROR; +} + +bool QWindowsFontEngine::hasCMapTable() const +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('c', 'm', 'a', 'p'), 0, 0, 0) != GDI_ERROR; +} + +bool QWindowsFontEngine::hasGlyfTable() const +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('g', 'l', 'y', 'f'), 0, 0, 0) != GDI_ERROR; +} + +bool QWindowsFontEngine::hasEbdtTable() const +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('E', 'B', 'D', 'T'), 0, 0, 0) != GDI_ERROR; +} + +static inline QString stringFromOutLineTextMetric(const OUTLINETEXTMETRIC *otm, PSTR offset) +{ + const uchar *p = reinterpret_cast<const uchar *>(otm) + quintptr(offset); + return QString::fromWCharArray(reinterpret_cast<const wchar_t *>(p)); +} + +void QWindowsFontEngine::getCMap() +{ + ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE) || hasCMapTable(); + + cffTable = hasCFFTable(); + + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + bool symb = false; + if (ttf) { + cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p')); + cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), + cmapTable.size(), &symb, &cmapSize); + } + if (!cmap) { + ttf = false; + symb = false; + } + symbol = symb; + designToDevice = 1; + _faceId.index = 0; + if(cmap) { + OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); + unitsPerEm = int(otm->otmEMSquare); + const QFixed unitsPerEmF(unitsPerEm); + designToDevice = unitsPerEmF / QFixed::fromReal(fontDef.pixelSize); + x_height = int(otm->otmsXHeight); + loadKerningPairs(designToDevice); + _faceId.filename = QFile::encodeName(stringFromOutLineTextMetric(otm, otm->otmpFullName)); + lineWidth = otm->otmsUnderscoreSize; + fsType = otm->otmfsType; + free(otm); + + } else { + unitsPerEm = tm.tmHeight; + } +} + +int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs) const +{ + int glyph_pos = 0; + { + if (symbol) { + QStringIterator it(str, str + numChars); + while (it.hasNext()) { + const uint uc = it.next(); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc); + if(!glyphs->glyphs[glyph_pos] && uc < 0x100) + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc + 0xf000); + ++glyph_pos; + } + } else if (ttf) { + QStringIterator it(str, str + numChars); + while (it.hasNext()) { + const uint uc = it.next(); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc); + ++glyph_pos; + } + } else { + QStringIterator it(str, str + numChars); + while (it.hasNext()) { + const uint uc = it.next(); + if (uc >= tm.tmFirstChar && uc <= tm.tmLastChar) + glyphs->glyphs[glyph_pos] = uc; + else + glyphs->glyphs[glyph_pos] = 0; + ++glyph_pos; + } + } + } + glyphs->numGlyphs = glyph_pos; + return glyph_pos; +} + +/*! + \class QWindowsFontEngine + \brief Standard Windows font engine. + \internal + \ingroup qt-lighthouse-win + + Will probably be superseded by a common Free Type font engine in Qt 5.X. +*/ + +QWindowsFontEngine::QWindowsFontEngine(const QString &name, + LOGFONT lf, + const QSharedPointer<QWindowsFontEngineData> &fontEngineData) + : QFontEngine(Win), + m_fontEngineData(fontEngineData), + _name(name), + hfont(0), + m_logfont(lf), + ttf(0), + hasOutline(0), + cmap(0), + cmapSize(0), + lbearing(SHRT_MIN), + rbearing(SHRT_MIN), + x_height(-1), + synthesized_flags(-1), + lineWidth(-1), + widthCache(0), + widthCacheSize(0), + designAdvances(0), + designAdvancesSize(0) +{ + qCDebug(lcQpaFonts) << __FUNCTION__ << name << lf.lfHeight; + hfont = CreateFontIndirect(&m_logfont); + if (!hfont) { + qErrnoWarning("%s: CreateFontIndirect failed for family '%s'", __FUNCTION__, qPrintable(name)); + hfont = QWindowsFontDatabase::systemFont(); + } + + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + const BOOL res = GetTextMetrics(hdc, &tm); + if (!res) { + qErrnoWarning("%s: GetTextMetrics failed", __FUNCTION__); + ZeroMemory(&tm, sizeof(TEXTMETRIC)); + } + + fontDef.pixelSize = -lf.lfHeight; + fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); + + cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000; + getCMap(); + + if (!resolvedGetCharWidthI) + resolveGetCharWidthI(); + + // ### Properties accessed by QWin32PrintEngine (QtPrintSupport) + QVariantMap userData; + userData.insert(QStringLiteral("logFont"), QVariant::fromValue(m_logfont)); + userData.insert(QStringLiteral("hFont"), QVariant::fromValue(hfont)); + userData.insert(QStringLiteral("trueType"), QVariant(bool(ttf))); + setUserData(userData); + + hasUnreliableOutline = hasGlyfTable() && hasEbdtTable(); +} + +QWindowsFontEngine::~QWindowsFontEngine() +{ + if (designAdvances) + free(designAdvances); + + if (widthCache) + free(widthCache); + + // make sure we aren't by accident still selected + SelectObject(m_fontEngineData->hdc, QWindowsFontDatabase::systemFont()); + + if (!DeleteObject(hfont)) + qErrnoWarning("%s: QFontEngineWin: failed to delete font...", __FUNCTION__); + qCDebug(lcQpaFonts) << __FUNCTION__ << _name; + + if (!uniqueFamilyName.isEmpty()) { + if (QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration()) { + QPlatformFontDatabase *pfdb = pi->fontDatabase(); + static_cast<QWindowsFontDatabase *>(pfdb)->derefUniqueFont(uniqueFamilyName); + } + } +} + +glyph_t QWindowsFontEngine::glyphIndex(uint ucs4) const +{ + glyph_t glyph = 0; + + if (symbol) { + glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4); + if (glyph == 0 && ucs4 < 0x100) + glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4 + 0xf000); + } else if (ttf) { + glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4); + } else if (ucs4 >= tm.tmFirstChar && ucs4 <= tm.tmLastChar) { + glyph = ucs4; + } + + return glyph; +} + +HGDIOBJ QWindowsFontEngine::selectDesignFont() const +{ + LOGFONT f = m_logfont; + f.lfHeight = -unitsPerEm; + f.lfWidth = 0; + HFONT designFont = CreateFontIndirect(&f); + return SelectObject(m_fontEngineData->hdc, designFont); +} + +bool QWindowsFontEngine::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; + } + + glyphs->numGlyphs = *nglyphs; + *nglyphs = getGlyphIndexes(str, len, glyphs); + + if (!(flags & GlyphIndicesOnly)) + recalcAdvances(glyphs, flags); + + return true; +} + +inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width) +{ + if (ptrGetCharWidthI) + ptrGetCharWidthI(hdc, glyph, 1, 0, &width); +} + +void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const +{ + HGDIOBJ oldFont = 0; + HDC hdc = m_fontEngineData->hdc; + if (ttf && (flags & DesignMetrics)) { + for(int i = 0; i < glyphs->numGlyphs; i++) { + unsigned int glyph = glyphs->glyphs[i]; + if(int(glyph) >= designAdvancesSize) { + const int newSize = int(glyph + 256) >> 8 << 8; + designAdvances = reinterpret_cast<QFixed *>(realloc(designAdvances, size_t(newSize) * sizeof(QFixed))); + Q_CHECK_PTR(designAdvances); + for(int i = designAdvancesSize; i < newSize; ++i) + designAdvances[i] = -1000000; + designAdvancesSize = newSize; + } + if (designAdvances[glyph] < -999999) { + if (!oldFont) + oldFont = selectDesignFont(); + + int width = 0; + calculateTTFGlyphWidth(hdc, glyph, width); + designAdvances[glyph] = QFixed(width) / designToDevice; + } + glyphs->advances[i] = designAdvances[glyph]; + } + if(oldFont) + DeleteObject(SelectObject(hdc, oldFont)); + } else { + for(int i = 0; i < glyphs->numGlyphs; i++) { + unsigned int glyph = glyphs->glyphs[i]; + + if (glyph >= widthCacheSize) { + const uint newSize = (glyph + 256) >> 8 << 8; + widthCache = reinterpret_cast<unsigned char *>(realloc(widthCache, newSize * sizeof(QFixed))); + Q_CHECK_PTR(widthCache); + memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize); + widthCacheSize = newSize; + } + glyphs->advances[i] = widthCache[glyph]; + // font-width cache failed + if (glyphs->advances[i].value() == 0) { + int width = 0; + if (!oldFont) + oldFont = SelectObject(hdc, hfont); + + if (!ttf) { + QChar ch[2] = { ushort(glyph), 0 }; + int chrLen = 1; + if (QChar::requiresSurrogates(glyph)) { + ch[0] = QChar::highSurrogate(glyph); + ch[1] = QChar::lowSurrogate(glyph); + ++chrLen; + } + SIZE size = {0, 0}; + GetTextExtentPoint32(hdc, reinterpret_cast<const wchar_t *>(ch), chrLen, &size); + width = size.cx; + } else { + calculateTTFGlyphWidth(hdc, glyph, width); + } + glyphs->advances[i] = width; + // if glyph's within cache range, store it for later + if (width > 0 && width < 0x100) + widthCache[glyph] = uchar(width); + } + } + + if (oldFont) + SelectObject(hdc, oldFont); + } +} + +glyph_metrics_t QWindowsFontEngine::boundingBox(const QGlyphLayout &glyphs) +{ + if (glyphs.numGlyphs == 0) + return glyph_metrics_t(); + + QFixed w = 0; + for (int i = 0; i < glyphs.numGlyphs; ++i) + w += glyphs.effectiveAdvance(i); + + return glyph_metrics_t(0, -tm.tmAscent, w - lastRightBearing(glyphs), tm.tmHeight, w, 0); +} + +bool QWindowsFontEngine::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const +{ + Q_ASSERT(metrics != 0); + + HDC hdc = m_fontEngineData->hdc; + + GLYPHMETRICS gm; + DWORD res = 0; + MAT2 mat; + mat.eM11.value = mat.eM22.value = 1; + mat.eM11.fract = mat.eM22.fract = 0; + mat.eM21.value = mat.eM12.value = 0; + mat.eM21.fract = mat.eM12.fract = 0; + + if (t.type() > QTransform::TxTranslate) { + // We need to set the transform using the HDC's world + // matrix rather than using the MAT2 above, because the + // results provided when transforming via MAT2 does not + // match the glyphs that are drawn using a WorldTransform + XFORM xform; + xform.eM11 = FLOAT(t.m11()); + xform.eM12 = FLOAT(t.m12()); + xform.eM21 = FLOAT(t.m21()); + xform.eM22 = FLOAT(t.m22()); + xform.eDx = 0; + xform.eDy = 0; + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + } + + uint format = GGO_METRICS; + if (ttf) + format |= GGO_GLYPH_INDEX; + res = GetGlyphOutline(hdc, glyph, format, &gm, 0, 0, &mat); + + if (t.type() > QTransform::TxTranslate) { + XFORM xform; + xform.eM11 = xform.eM22 = 1; + xform.eM12 = xform.eM21 = xform.eDx = xform.eDy = 0; + SetWorldTransform(hdc, &xform); + SetGraphicsMode(hdc, GM_COMPATIBLE); + } + + if (res != GDI_ERROR) { + *metrics = glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y, + int(gm.gmBlackBoxX), int(gm.gmBlackBoxY), + gm.gmCellIncX, gm.gmCellIncY); + return true; + } else { + return false; + } +} + +glyph_metrics_t QWindowsFontEngine::boundingBox(glyph_t glyph, const QTransform &t) +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + + glyph_metrics_t glyphMetrics; + bool success = getOutlineMetrics(glyph, t, &glyphMetrics); + + if (!ttf && !success) { + // Bitmap fonts + wchar_t ch = wchar_t(glyph); + ABCFLOAT abc; + GetCharABCWidthsFloat(hdc, ch, ch, &abc); + int width = qRound(abc.abcfB); + + return glyph_metrics_t(QFixed::fromReal(abc.abcfA), -tm.tmAscent, width, tm.tmHeight, width, 0).transformed(t); + } + + return glyphMetrics; +} + +QFixed QWindowsFontEngine::ascent() const +{ + return tm.tmAscent; +} + +QFixed QWindowsFontEngine::descent() const +{ + return tm.tmDescent; +} + +QFixed QWindowsFontEngine::leading() const +{ + return tm.tmExternalLeading; +} + +namespace { +# pragma pack(1) + + struct OS2Table + { + quint16 version; + qint16 avgCharWidth; + quint16 weightClass; + quint16 widthClass; + quint16 type; + qint16 subscriptXSize; + qint16 subscriptYSize; + qint16 subscriptXOffset; + qint16 subscriptYOffset; + qint16 superscriptXSize; + qint16 superscriptYSize; + qint16 superscriptXOffset; + qint16 superscriptYOffset; + qint16 strikeOutSize; + qint16 strikeOutPosition; + qint16 familyClass; + quint8 panose[10]; + quint32 unicodeRanges[4]; + quint8 vendorID[4]; + quint16 selection; + quint16 firstCharIndex; + quint16 lastCharIndex; + qint16 typoAscender; + qint16 typoDescender; + qint16 typoLineGap; + quint16 winAscent; + quint16 winDescent; + quint32 codepageRanges[2]; + qint16 height; + qint16 capHeight; + quint16 defaultChar; + quint16 breakChar; + quint16 maxContext; + }; + +# pragma pack() +} + +QFixed QWindowsFontEngine::capHeight() const +{ + const QByteArray tableData = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); + if (size_t(tableData.size()) >= sizeof(OS2Table)) { + const OS2Table *table = reinterpret_cast<const OS2Table *>(tableData.constData()); + if (qFromBigEndian<quint16>(table->version) >= 2) { + qint16 capHeight = qFromBigEndian<qint16>(table->capHeight); + if (capHeight > 0) + return QFixed(capHeight) / designToDevice; + } + } + return calculatedCapHeight(); +} + +QFixed QWindowsFontEngine::xHeight() const +{ + if(x_height >= 0) + return x_height; + return QFontEngine::xHeight(); +} + +QFixed QWindowsFontEngine::averageCharWidth() const +{ + return tm.tmAveCharWidth; +} + +qreal QWindowsFontEngine::maxCharWidth() const +{ + return tm.tmMaxCharWidth; +} + +enum { max_font_count = 256 }; +static const ushort char_table[] = { + 40, + 67, + 70, + 75, + 86, + 88, + 89, + 91, + 102, + 114, + 124, + 127, + 205, + 645, + 884, + 922, + 1070, + 12386, + 0 +}; + +static const int char_table_entries = sizeof(char_table)/sizeof(ushort); + +#ifndef Q_CC_MINGW +void QWindowsFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + + if (ttf) { + ABC abcWidths; + GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths); + if (leftBearing) + *leftBearing = abcWidths.abcA; + if (rightBearing) + *rightBearing = abcWidths.abcC; + } else { + QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing); + } +} +#endif // Q_CC_MINGW + +bool QWindowsFontEngine::hasUnreliableGlyphOutline() const +{ + return hasUnreliableOutline || QFontEngine::hasUnreliableGlyphOutline(); +} + +qreal QWindowsFontEngine::minLeftBearing() const +{ + if (lbearing == SHRT_MIN) + minRightBearing(); // calculates both + + return lbearing; +} + +qreal QWindowsFontEngine::minRightBearing() const +{ + if (rbearing == SHRT_MIN) { + int ml = 0; + int mr = 0; + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + if (ttf) { + ABC *abc = 0; + int n = tm.tmLastChar - tm.tmFirstChar; + if (n <= max_font_count) { + abc = new ABC[n+1]; + GetCharABCWidths(hdc, tm.tmFirstChar, tm.tmLastChar, abc); + } else { + abc = new ABC[char_table_entries+1]; + for(int i = 0; i < char_table_entries; i++) + GetCharABCWidths(hdc, char_table[i], char_table[i], abc + i); + n = char_table_entries; + } + ml = abc[0].abcA; + mr = abc[0].abcC; + for (int i = 1; i < n; i++) { + if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) { + ml = qMin(ml,abc[i].abcA); + mr = qMin(mr,abc[i].abcC); + } + } + delete [] abc; + } else { + ABCFLOAT *abc = 0; + int n = tm.tmLastChar - tm.tmFirstChar+1; + if (n <= max_font_count) { + abc = new ABCFLOAT[n]; + GetCharABCWidthsFloat(hdc, tm.tmFirstChar, tm.tmLastChar, abc); + } else { + abc = new ABCFLOAT[char_table_entries]; + for(int i = 0; i < char_table_entries; i++) + GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i); + n = char_table_entries; + } + float fml = abc[0].abcfA; + float fmr = abc[0].abcfC; + for (int i=1; i<n; i++) { + if (abc[i].abcfA + abc[i].abcfB + abc[i].abcfC != 0) { + fml = qMin(fml,abc[i].abcfA); + fmr = qMin(fmr,abc[i].abcfC); + } + } + ml = int(fml - 0.9999); + mr = int(fmr - 0.9999); + delete [] abc; + } + lbearing = ml; + rbearing = mr; + } + + return rbearing; +} + +static inline double qt_fixed_to_double(const FIXED &p) { + return ((p.value << 16) + p.fract) / 65536.0; +} + +static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale) { + return QPointF(qt_fixed_to_double(pt.x) * scale, -qt_fixed_to_double(pt.y) * scale); +} + +#ifndef GGO_UNHINTED +#define GGO_UNHINTED 0x0100 +#endif + +static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc, + QPainterPath *path, bool ttf, glyph_metrics_t *metric = 0, qreal scale = 1) +{ + MAT2 mat; + mat.eM11.value = mat.eM22.value = 1; + mat.eM11.fract = mat.eM22.fract = 0; + mat.eM21.value = mat.eM12.value = 0; + mat.eM21.fract = mat.eM12.fract = 0; + + GLYPHMETRICS gMetric; + memset(&gMetric, 0, sizeof(GLYPHMETRICS)); + + if (metric) { + // If metrics requested, retrieve first using GGO_METRICS, because the returned + // values are incorrect for OpenType PS fonts if obtained at the same time as the + // glyph paths themselves (ie. with GGO_NATIVE as the format). + uint format = GGO_METRICS; + if (ttf) + format |= GGO_GLYPH_INDEX; + if (GetGlyphOutline(hdc, glyph, format, &gMetric, 0, 0, &mat) == GDI_ERROR) + return false; + // #### obey scale + *metric = glyph_metrics_t(gMetric.gmptGlyphOrigin.x, -gMetric.gmptGlyphOrigin.y, + int(gMetric.gmBlackBoxX), int(gMetric.gmBlackBoxY), + gMetric.gmCellIncX, gMetric.gmCellIncY); + } + + uint glyphFormat = GGO_NATIVE; + + if (ttf) + glyphFormat |= GGO_GLYPH_INDEX; + + const DWORD bufferSize = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat); + if (bufferSize == GDI_ERROR) + return false; + + char *dataBuffer = new char[bufferSize]; + DWORD ret = GDI_ERROR; + ret = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, bufferSize, dataBuffer, &mat); + if (ret == GDI_ERROR) { + delete [] dataBuffer; + return false; + } + + DWORD offset = 0; + DWORD headerOffset = 0; + + QPointF oset = position.toPointF(); + while (headerOffset < bufferSize) { + const TTPOLYGONHEADER *ttph = reinterpret_cast<const TTPOLYGONHEADER *>(dataBuffer + headerOffset); + + QPointF lastPoint(qt_to_qpointf(ttph->pfxStart, scale)); + path->moveTo(lastPoint + oset); + offset += sizeof(TTPOLYGONHEADER); + while (offset < headerOffset + ttph->cb) { + const TTPOLYCURVE *curve = reinterpret_cast<const TTPOLYCURVE *>(dataBuffer + offset); + switch (curve->wType) { + case TT_PRIM_LINE: { + for (int i=0; i<curve->cpfx; ++i) { + QPointF p = qt_to_qpointf(curve->apfx[i], scale) + oset; + path->lineTo(p); + } + break; + } + case TT_PRIM_QSPLINE: { + const QPainterPath::Element &elm = path->elementAt(path->elementCount()-1); + QPointF prev(elm.x, elm.y); + QPointF endPoint; + for (int i=0; i<curve->cpfx - 1; ++i) { + QPointF p1 = qt_to_qpointf(curve->apfx[i], scale) + oset; + QPointF p2 = qt_to_qpointf(curve->apfx[i+1], scale) + oset; + if (i < curve->cpfx - 2) { + endPoint = QPointF((p1.x() + p2.x()) / 2, (p1.y() + p2.y()) / 2); + } else { + endPoint = p2; + } + + path->quadTo(p1, endPoint); + prev = endPoint; + } + + break; + } + case TT_PRIM_CSPLINE: { + for (int i=0; i<curve->cpfx; ) { + QPointF p2 = qt_to_qpointf(curve->apfx[i++], scale) + oset; + QPointF p3 = qt_to_qpointf(curve->apfx[i++], scale) + oset; + QPointF p4 = qt_to_qpointf(curve->apfx[i++], scale) + oset; + path->cubicTo(p2, p3, p4); + } + break; + } + default: + qWarning("QFontEngineWin::addOutlineToPath, unhandled switch case"); + } + offset += sizeof(TTPOLYCURVE) + (curve->cpfx-1) * sizeof(POINTFX); + } + path->closeSubpath(); + headerOffset += ttph->cb; + } + delete [] dataBuffer; + + return true; +} + +void QWindowsFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags) +{ + LOGFONT lf = m_logfont; + // The sign must be negative here to make sure we match against character height instead of + // hinted cell height. This ensures that we get linear matching, and we need this for + // paths since we later on apply a scaling transform to the glyph outline to get the + // font at the correct pixel size. + lf.lfHeight = -unitsPerEm; + lf.lfWidth = 0; + HFONT hf = CreateFontIndirect(&lf); + HDC hdc = m_fontEngineData->hdc; + HGDIOBJ oldfont = SelectObject(hdc, hf); + + for(int i = 0; i < nglyphs; ++i) { + if (!addGlyphToPath(glyphs[i], positions[i], hdc, path, ttf, /*metric*/0, + qreal(fontDef.pixelSize) / unitsPerEm)) { + // Some windows fonts, like "Modern", are vector stroke + // fonts, which are reported as TMPF_VECTOR but do not + // support GetGlyphOutline, and thus we set this bit so + // that addOutLineToPath can check it and return safely... + hasOutline = false; + break; + } + } + DeleteObject(SelectObject(hdc, oldfont)); +} + +void QWindowsFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, + QPainterPath *path, QTextItem::RenderFlags flags) +{ + if(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) { + hasOutline = true; + QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); + if (hasOutline) { + // has_outline is set to false if addGlyphToPath gets + // false from GetGlyphOutline, meaning its not an outline + // font. + return; + } + } + QFontEngine::addBitmapFontToPath(x, y, glyphs, path, flags); +} + +QFontEngine::FaceId QWindowsFontEngine::faceId() const +{ + return _faceId; +} + +QT_BEGIN_INCLUDE_NAMESPACE +#include <qdebug.h> +QT_END_INCLUDE_NAMESPACE + +int QWindowsFontEngine::synthesized() const +{ + if(synthesized_flags == -1) { + synthesized_flags = 0; + if(ttf) { + const DWORD HEAD = MAKE_LITTLE_ENDIAN_TAG('h', 'e', 'a', 'd'); + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + uchar data[4]; + GetFontData(hdc, HEAD, 44, &data, 4); + USHORT macStyle = getUShort(data); + if (tm.tmItalic && !(macStyle & 2)) + synthesized_flags = SynthesizedItalic; + if (fontDef.stretch != 100 && ttf) + synthesized_flags |= SynthesizedStretch; + if (tm.tmWeight >= 500 && !(macStyle & 1)) + synthesized_flags |= SynthesizedBold; + //qDebug() << "font is" << _name << + // "it=" << (macStyle & 2) << fontDef.style << "flags=" << synthesized_flags; + } + } + return synthesized_flags; +} + +QFixed QWindowsFontEngine::emSquareSize() const +{ + return unitsPerEm; +} + +QFontEngine::Properties QWindowsFontEngine::properties() const +{ + LOGFONT lf = m_logfont; + lf.lfHeight = unitsPerEm; + HFONT hf = CreateFontIndirect(&lf); + HDC hdc = m_fontEngineData->hdc; + HGDIOBJ oldfont = SelectObject(hdc, hf); + OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); + Properties p; + p.emSquare = unitsPerEm; + p.italicAngle = otm->otmItalicAngle; + const QByteArray name = stringFromOutLineTextMetric(otm, otm->otmpFamilyName).toLatin1() + + stringFromOutLineTextMetric(otm, otm->otmpStyleName).toLatin1(); + p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(name); + p.boundingBox = QRectF(otm->otmrcFontBox.left, -otm->otmrcFontBox.top, + otm->otmrcFontBox.right - otm->otmrcFontBox.left, + otm->otmrcFontBox.top - otm->otmrcFontBox.bottom); + p.ascent = otm->otmAscent; + p.descent = -otm->otmDescent; + p.leading = int(otm->otmLineGap); + p.capHeight = 0; + p.lineWidth = otm->otmsUnderscoreSize; + free(otm); + DeleteObject(SelectObject(hdc, oldfont)); + return p; +} + +void QWindowsFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) +{ + LOGFONT lf = m_logfont; + lf.lfHeight = -unitsPerEm; + int flags = synthesized(); + if(flags & SynthesizedItalic) + lf.lfItalic = false; + lf.lfWidth = 0; + HFONT hf = CreateFontIndirect(&lf); + HDC hdc = m_fontEngineData->hdc; + HGDIOBJ oldfont = SelectObject(hdc, hf); + QFixedPoint p; + p.x = 0; + p.y = 0; + addGlyphToPath(glyph, p, hdc, path, ttf, metrics); + DeleteObject(SelectObject(hdc, oldfont)); +} + +bool QWindowsFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + if (!ttf && !cffTable) + return false; + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + DWORD t = qbswap<quint32>(tag); + *length = GetFontData(hdc, t, 0, buffer, *length); + Q_ASSERT(*length == GDI_ERROR || int(*length) > 0); + return *length != GDI_ERROR; +} + +#if !defined(CLEARTYPE_QUALITY) +# define CLEARTYPE_QUALITY 5 +#endif + +QWindowsNativeImage *QWindowsFontEngine::drawGDIGlyph(HFONT font, glyph_t glyph, int margin, + const QTransform &t, + QImage::Format mask_format) +{ + Q_UNUSED(mask_format) + glyph_metrics_t gm = boundingBox(glyph); + +// printf(" -> for glyph %4x\n", glyph); + + int gx = gm.x.toInt(); + int gy = gm.y.toInt(); + int iw = gm.width.toInt(); + int ih = gm.height.toInt(); + + if (iw <= 0 || ih <= 0) + return 0; + + bool has_transformation = t.type() > QTransform::TxTranslate; + + unsigned int options = ttf ? ETO_GLYPH_INDEX : 0; + XFORM xform; + + if (has_transformation) { + xform.eM11 = FLOAT(t.m11()); + xform.eM12 = FLOAT(t.m12()); + xform.eM21 = FLOAT(t.m21()); + xform.eM22 = FLOAT(t.m22()); + xform.eDx = margin; + xform.eDy = margin; + + const HDC hdc = m_fontEngineData->hdc; + + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + HGDIOBJ old_font = SelectObject(hdc, font); + + const UINT ggo_options = GGO_METRICS | (ttf ? GGO_GLYPH_INDEX : 0); + GLYPHMETRICS tgm; + MAT2 mat; + memset(&mat, 0, sizeof(mat)); + mat.eM11.value = mat.eM22.value = 1; + + const DWORD result = GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat); + + XFORM identity = {1, 0, 0, 1, 0, 0}; + SetWorldTransform(hdc, &identity); + SetGraphicsMode(hdc, GM_COMPATIBLE); + SelectObject(hdc, old_font); + + if (result == GDI_ERROR) { + const int errorCode = int(GetLastError()); + qErrnoWarning(errorCode, "QWinFontEngine: unable to query transformed glyph metrics (GetGlyphOutline() failed, error %d)...", errorCode); + return 0; + } + + iw = int(tgm.gmBlackBoxX); + ih = int(tgm.gmBlackBoxY); + + xform.eDx -= tgm.gmptGlyphOrigin.x; + xform.eDy += tgm.gmptGlyphOrigin.y; + } + + // The padding here needs to be kept in sync with the values in alphaMapBoundingBox. + QWindowsNativeImage *ni = new QWindowsNativeImage(iw + 2 * margin, + ih + 2 * margin, + QWindowsNativeImage::systemFormat()); + + /*If cleartype is enabled we use the standard system format even on Windows CE + and not the special textbuffer format we have to use if cleartype is disabled*/ + + ni->image().fill(0xffffffff); + + HDC hdc = ni->hdc(); + + SelectObject(hdc, GetStockObject(NULL_BRUSH)); + SelectObject(hdc, GetStockObject(BLACK_PEN)); + SetTextColor(hdc, RGB(0,0,0)); + SetBkMode(hdc, TRANSPARENT); + SetTextAlign(hdc, TA_BASELINE); + + HGDIOBJ old_font = SelectObject(hdc, font); + + if (has_transformation) { + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + ExtTextOut(hdc, 0, 0, options, 0, reinterpret_cast<LPCWSTR>(&glyph), 1, 0); + } else { + ExtTextOut(hdc, -gx + margin, -gy + margin, options, 0, reinterpret_cast<LPCWSTR>(&glyph), 1, 0); + } + + SelectObject(hdc, old_font); + return ni; +} + +glyph_metrics_t QWindowsFontEngine::alphaMapBoundingBox(glyph_t glyph, QFixed, const QTransform &matrix, GlyphFormat format) +{ + int margin = 0; + if (format == QFontEngine::Format_A32 || format == QFontEngine::Format_ARGB) + margin = glyphMargin(QFontEngine::Format_A32); + glyph_metrics_t gm = boundingBox(glyph, matrix); + gm.width += margin * 2; + gm.height += margin * 2; + return gm; +} + +QImage QWindowsFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &xform) +{ + HFONT font = hfont; + + bool clearTypeTemporarilyDisabled = (m_fontEngineData->clearTypeEnabled && m_logfont.lfQuality != NONANTIALIASED_QUALITY); + if (clearTypeTemporarilyDisabled) { + LOGFONT lf = m_logfont; + lf.lfQuality = ANTIALIASED_QUALITY; + font = CreateFontIndirect(&lf); + } + QImage::Format mask_format = QWindowsNativeImage::systemFormat(); + mask_format = QImage::Format_RGB32; + + const QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform, mask_format); + if (mask == 0) { + if (m_fontEngineData->clearTypeEnabled) + DeleteObject(font); + return QImage(); + } + + QImage alphaMap(mask->width(), mask->height(), QImage::Format_Alpha8); + + + // Copy data... Cannot use QPainter here as GDI has messed up the + // Alpha channel of the ni.image pixels... + for (int y=0; y<mask->height(); ++y) { + uchar *dest = alphaMap.scanLine(y); + if (mask->image().format() == QImage::Format_RGB16) { + const qint16 *src = reinterpret_cast<const qint16 *>(mask->image().constScanLine(y)); + for (int x=0; x<mask->width(); ++x) + dest[x] = 255 - qGray(src[x]); + } else { + const uint *src = reinterpret_cast<const uint *>(mask->image().constScanLine(y)); + for (int x=0; x<mask->width(); ++x) { + if (QWindowsNativeImage::systemFormat() == QImage::Format_RGB16) + dest[x] = 255 - qGray(src[x]); + else + dest[x] = 255 - (m_fontEngineData->pow_gamma[qGray(src[x])] * 255. / 2047.); + } + } + } + + // Cleanup... + delete mask; + if (clearTypeTemporarilyDisabled) { + DeleteObject(font); + } + + return alphaMap; +} + +#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C +#define SPI_SETFONTSMOOTHINGCONTRAST 0x200D + +QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed, const QTransform &t) +{ + HFONT font = hfont; + + UINT contrast; + SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0); + SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, reinterpret_cast<void *>(quintptr(1000)), 0); + + int margin = glyphMargin(QFontEngine::Format_A32); + QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32); + SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, reinterpret_cast<void *>(quintptr(contrast)), 0); + + if (mask == 0) + return QImage(); + + // Gracefully handle the odd case when the display is 16-bit + const QImage source = mask->image().depth() == 32 + ? mask->image() + : mask->image().convertToFormat(QImage::Format_RGB32); + + QImage rgbMask(mask->width(), mask->height(), QImage::Format_RGB32); + for (int y=0; y<mask->height(); ++y) { + uint *dest = (uint *) rgbMask.scanLine(y); + const uint *src = reinterpret_cast<const uint *>(source.constScanLine(y)); + for (int x=0; x<mask->width(); ++x) { + dest[x] = 0xffffffff - (0x00ffffff & src[x]); + } + } + + delete mask; + + return rgbMask; +} + +QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const +{ + QFontDef request = fontDef; + QString actualFontName = request.family; + if (!uniqueFamilyName.isEmpty()) + request.family = uniqueFamilyName; + request.pixelSize = pixelSize; + + QFontEngine *fontEngine = + QWindowsFontDatabase::createEngine(request, + QWindowsFontDatabase::defaultVerticalDPI(), + m_fontEngineData); + if (fontEngine) { + fontEngine->fontDef.family = actualFontName; + if (!uniqueFamilyName.isEmpty()) { + static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName); + if (QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration()) { + QPlatformFontDatabase *pfdb = pi->fontDatabase(); + static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(uniqueFamilyName); + } + } + } + return fontEngine; +} + +Qt::HANDLE QWindowsFontEngine::handle() const +{ + return hfont; +} + +void QWindowsFontEngine::initFontInfo(const QFontDef &request, + int dpi) +{ + fontDef = request; // most settings are equal + HDC dc = m_fontEngineData->hdc; + SelectObject(dc, hfont); + wchar_t n[64]; + GetTextFace(dc, 64, n); + fontDef.family = QString::fromWCharArray(n); + fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); + if (fontDef.pointSize < 0) { + fontDef.pointSize = fontDef.pixelSize * 72. / dpi; + } else if (fontDef.pixelSize == -1) { + fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.); + } +} + +/*! + \class QWindowsMultiFontEngine + \brief Standard Windows Multi font engine. + \internal + \ingroup qt-lighthouse-win + + "Merges" several font engines that have gaps in the + supported writing systems. + + Will probably be superseded by a common Free Type font engine in Qt 5.X. +*/ +QWindowsMultiFontEngine::QWindowsMultiFontEngine(QFontEngine *fe, int script) + : QFontEngineMulti(fe, script) +{ +} + +QFontEngine *QWindowsMultiFontEngine::loadEngine(int at) +{ + QFontEngine *fontEngine = engine(0); + QSharedPointer<QWindowsFontEngineData> data; + LOGFONT lf; + +#ifndef QT_NO_DIRECTWRITE + if (fontEngine->type() == QFontEngine::DirectWrite) { + QWindowsFontEngineDirectWrite *fe = static_cast<QWindowsFontEngineDirectWrite *>(fontEngine); + lf = QWindowsFontDatabase::fontDefToLOGFONT(fe->fontDef); + + data = fe->fontEngineData(); + } else +#endif + { + QWindowsFontEngine *fe = static_cast<QWindowsFontEngine*>(fontEngine); + lf = fe->m_logfont; + + data = fe->fontEngineData(); + } + + const QString fam = fallbackFamilyAt(at - 1); + const int faceNameLength = qMin(fam.length(), LF_FACESIZE - 1); + memcpy(lf.lfFaceName, fam.utf16(), faceNameLength * sizeof(wchar_t)); + lf.lfFaceName[faceNameLength] = 0; + +#ifndef QT_NO_DIRECTWRITE + if (fontEngine->type() == QFontEngine::DirectWrite) { + const QString nameSubstitute = QWindowsFontEngineDirectWrite::fontNameSubstitute(fam); + if (nameSubstitute != fam) { + const int nameSubstituteLength = qMin(nameSubstitute.length(), LF_FACESIZE - 1); + memcpy(lf.lfFaceName, nameSubstitute.utf16(), nameSubstituteLength * sizeof(wchar_t)); + lf.lfFaceName[nameSubstituteLength] = 0; + } + + IDWriteFont *directWriteFont = 0; + HRESULT hr = data->directWriteGdiInterop->CreateFontFromLOGFONT(&lf, &directWriteFont); + if (FAILED(hr)) { + qErrnoWarning("%s: CreateFontFromLOGFONT failed", __FUNCTION__); + } else { + Q_ASSERT(directWriteFont); + IDWriteFontFace *directWriteFontFace = NULL; + HRESULT hr = directWriteFont->CreateFontFace(&directWriteFontFace); + if (SUCCEEDED(hr)) { + Q_ASSERT(directWriteFontFace); + QWindowsFontEngineDirectWrite *fedw = new QWindowsFontEngineDirectWrite(directWriteFontFace, + fontEngine->fontDef.pixelSize, + data); + fedw->fontDef.weight = fontEngine->fontDef.weight; + if (fontEngine->fontDef.style > QFont::StyleNormal) + fedw->fontDef.style = fontEngine->fontDef.style; + fedw->fontDef.family = fam; + fedw->fontDef.hintingPreference = fontEngine->fontDef.hintingPreference; + return fedw; + } else { + qErrnoWarning("%s: CreateFontFace failed", __FUNCTION__); + } + } + } +#endif + + // Get here if original font is not DirectWrite or DirectWrite creation failed for some + // reason + + QFontEngine *fe = new QWindowsFontEngine(fam, lf, data); + fe->fontDef.weight = fontEngine->fontDef.weight; + if (fontEngine->fontDef.style > QFont::StyleNormal) + fe->fontDef.style = fontEngine->fontDef.style; + fe->fontDef.family = fam; + fe->fontDef.hintingPreference = fontEngine->fontDef.hintingPreference; + return fe; +} + +bool QWindowsFontEngine::supportsTransformation(const QTransform &transform) const +{ + // Support all transformations for ttf files, and translations for raster fonts + return ttf || transform.type() <= QTransform::TxTranslate; +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontengine_p.h b/src/platformsupport/fontdatabases/windows/qwindowsfontengine_p.h new file mode 100644 index 0000000000..709de7d11d --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontengine_p.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** 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 QWINDOWSFONTENGINE_H +#define QWINDOWSFONTENGINE_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 <QtGui/private/qfontengine_p.h> + +#include <QtGui/QImage> +#include <QtCore/QSharedPointer> +#include <QtCore/QMetaType> + +#include <QtCore/qt_windows.h> + +QT_BEGIN_NAMESPACE + +class QWindowsNativeImage; +class QWindowsFontEngineData; + +class QWindowsFontEngine : public QFontEngine +{ + friend class QWindowsMultiFontEngine; + +public: + QWindowsFontEngine(const QString &name, LOGFONT lf, + const QSharedPointer<QWindowsFontEngineData> &fontEngineData); + + ~QWindowsFontEngine(); + void initFontInfo(const QFontDef &request, + int dpi); + + QFixed lineThickness() const Q_DECL_OVERRIDE; + Properties properties() const Q_DECL_OVERRIDE; + void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) Q_DECL_OVERRIDE; + FaceId faceId() const Q_DECL_OVERRIDE; + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const Q_DECL_OVERRIDE; + int synthesized() const Q_DECL_OVERRIDE; + QFixed emSquareSize() const Q_DECL_OVERRIDE; + + glyph_t glyphIndex(uint ucs4) const Q_DECL_OVERRIDE; + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const Q_DECL_OVERRIDE; + void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags) const Q_DECL_OVERRIDE; + + void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; + virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; + + HGDIOBJ selectDesignFont() const; + + glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) Q_DECL_OVERRIDE; + glyph_metrics_t boundingBox(glyph_t g) Q_DECL_OVERRIDE { return boundingBox(g, QTransform()); } + glyph_metrics_t boundingBox(glyph_t g, const QTransform &t) Q_DECL_OVERRIDE; + + + QFixed ascent() const Q_DECL_OVERRIDE; + QFixed descent() const Q_DECL_OVERRIDE; + QFixed leading() const Q_DECL_OVERRIDE; + QFixed xHeight() const Q_DECL_OVERRIDE; + QFixed capHeight() const Q_DECL_OVERRIDE; + QFixed averageCharWidth() const Q_DECL_OVERRIDE; + qreal maxCharWidth() const Q_DECL_OVERRIDE; + qreal minLeftBearing() const Q_DECL_OVERRIDE; + qreal minRightBearing() const Q_DECL_OVERRIDE; + + QImage alphaMapForGlyph(glyph_t t) Q_DECL_OVERRIDE { return alphaMapForGlyph(t, QTransform()); } + QImage alphaMapForGlyph(glyph_t, const QTransform &xform) Q_DECL_OVERRIDE; + QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, const QTransform &xform) Q_DECL_OVERRIDE; + glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed, const QTransform &matrix, GlyphFormat) Q_DECL_OVERRIDE; + + QFontEngine *cloneWithSize(qreal pixelSize) const Q_DECL_OVERRIDE; + Qt::HANDLE handle() const Q_DECL_OVERRIDE; + bool supportsTransformation(const QTransform &transform) const Q_DECL_OVERRIDE; + +#ifndef Q_CC_MINGW + void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0) Q_DECL_OVERRIDE; +#endif + + bool hasUnreliableGlyphOutline() const Q_DECL_OVERRIDE; + + int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs) const; + void getCMap(); + + bool getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const; + + const QSharedPointer<QWindowsFontEngineData> &fontEngineData() const { return m_fontEngineData; } + + void setUniqueFamilyName(const QString &newName) { uniqueFamilyName = newName; } + +private: + QWindowsNativeImage *drawGDIGlyph(HFONT font, glyph_t, int margin, const QTransform &xform, + QImage::Format mask_format); + bool hasCFFTable() const; + bool hasCMapTable() const; + bool hasGlyfTable() const; + bool hasEbdtTable() const; + + const QSharedPointer<QWindowsFontEngineData> m_fontEngineData; + + const QString _name; + QString uniqueFamilyName; + HFONT hfont; + const LOGFONT m_logfont; + uint ttf : 1; + uint hasOutline : 1; + uint hasUnreliableOutline : 1; + uint cffTable : 1; + TEXTMETRIC tm; + const unsigned char *cmap; + int cmapSize; + QByteArray cmapTable; + mutable qreal lbearing; + mutable qreal rbearing; + QFixed designToDevice; + int unitsPerEm; + QFixed x_height; + FaceId _faceId; + + mutable int synthesized_flags; + mutable QFixed lineWidth; + mutable unsigned char *widthCache; + mutable uint widthCacheSize; + mutable QFixed *designAdvances; + mutable int designAdvancesSize; +}; + +class QWindowsMultiFontEngine : public QFontEngineMulti +{ +public: + explicit QWindowsMultiFontEngine(QFontEngine *fe, int script); + + QFontEngine *loadEngine(int at) Q_DECL_OVERRIDE; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(HFONT) +Q_DECLARE_METATYPE(LOGFONT) + +#endif // QWINDOWSFONTENGINE_H diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp new file mode 100644 index 0000000000..f266e85126 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp @@ -0,0 +1,1000 @@ +/**************************************************************************** +** +** 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 QT_NO_DIRECTWRITE + +#include "qwindowsfontenginedirectwrite_p.h" +#include "qwindowsfontdatabase_p.h" + +#include <QtCore/QtEndian> +#include <QtCore/QVarLengthArray> +#include <QtCore/QFile> +#include <private/qstringiterator_p.h> +#include <QtCore/private/qsystemlibrary_p.h> +#include <QtGui/private/qguiapplication_p.h> +#include <qpa/qplatformintegration.h> + +#if defined(QT_USE_DIRECTWRITE2) +# include <dwrite_2.h> +#else +# include <dwrite.h> +#endif + +#include <d2d1.h> + +QT_BEGIN_NAMESPACE + +// Convert from design units to logical pixels +#define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE) \ + QFixed::fromReal((qreal(DESIGN_UNIT_VALUE) / qreal(m_unitsPerEm)) * fontDef.pixelSize) + +namespace { + + class GeometrySink: public IDWriteGeometrySink + { + public: + GeometrySink(QPainterPath *path) + : m_refCount(0), m_path(path) + { + Q_ASSERT(m_path != 0); + } + virtual ~GeometrySink() + { + } + + IFACEMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *beziers, UINT bezierCount); + IFACEMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointCount); + IFACEMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin); + IFACEMETHOD(Close)(); + IFACEMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd); + IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode); + IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags); + + IFACEMETHOD_(unsigned long, AddRef)(); + IFACEMETHOD_(unsigned long, Release)(); + IFACEMETHOD(QueryInterface)(IID const &riid, void **ppvObject); + + private: + inline static QPointF fromD2D1_POINT_2F(const D2D1_POINT_2F &inp) + { + return QPointF(inp.x, inp.y); + } + + unsigned long m_refCount; + QPointF m_startPoint; + QPainterPath *m_path; + }; + + void GeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, + UINT bezierCount) + { + for (uint i=0; i<bezierCount; ++i) { + QPointF c1 = fromD2D1_POINT_2F(beziers[i].point1); + QPointF c2 = fromD2D1_POINT_2F(beziers[i].point2); + QPointF p2 = fromD2D1_POINT_2F(beziers[i].point3); + + m_path->cubicTo(c1, c2, p2); + } + } + + void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) + { + for (uint i=0; i<pointsCount; ++i) + m_path->lineTo(fromD2D1_POINT_2F(points[i])); + } + + void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint, + D2D1_FIGURE_BEGIN /*figureBegin*/) + { + m_startPoint = fromD2D1_POINT_2F(startPoint); + m_path->moveTo(m_startPoint); + } + + IFACEMETHODIMP GeometrySink::Close() + { + return E_NOTIMPL; + } + + void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) + { + if (figureEnd == D2D1_FIGURE_END_CLOSED) + m_path->closeSubpath(); + } + + void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) + { + m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE + ? Qt::OddEvenFill + : Qt::WindingFill); + } + + void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/) + { + /* Not implemented */ + } + + IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + IFACEMETHODIMP_(unsigned long) GeometrySink::Release() + { + unsigned long newCount = InterlockedDecrement(&m_refCount); + if (newCount == 0) + { + delete this; + return 0; + } + + return newCount; + } + + IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject) + { + if (__uuidof(IDWriteGeometrySink) == riid) { + *ppvObject = this; + } else if (__uuidof(IUnknown) == riid) { + *ppvObject = this; + } else { + *ppvObject = NULL; + return E_FAIL; + } + + AddRef(); + return S_OK; + } + +} + +static DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(QFont::HintingPreference hintingPreference) +{ + switch (hintingPreference) { + case QFont::PreferNoHinting: + return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; + case QFont::PreferVerticalHinting: + return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; + default: + return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC; + } +} + +/*! + \class QWindowsFontEngineDirectWrite + \brief Windows font engine using Direct Write. + \internal + \ingroup qt-lighthouse-win + + Font engine for subpixel positioned text on Windows Vista + (with platform update) and Windows 7. If selected during + configuration, the engine will be selected only when the hinting + preference of a font is set to None or Vertical hinting. The font + database uses most of the same logic but creates a direct write + font based on the LOGFONT rather than a GDI handle. + + Will probably be superseded by a common Free Type font engine in Qt 5.X. +*/ + +QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace, + qreal pixelSize, + const QSharedPointer<QWindowsFontEngineData> &d) + : QFontEngine(DirectWrite) + , m_fontEngineData(d) + , m_directWriteFontFace(directWriteFontFace) + , m_directWriteBitmapRenderTarget(0) + , m_lineThickness(-1) + , m_unitsPerEm(-1) + , m_ascent(-1) + , m_capHeight(-1) + , m_descent(-1) + , m_xHeight(-1) + , m_lineGap(-1) +{ + qCDebug(lcQpaFonts) << __FUNCTION__ << pixelSize; + + Q_ASSERT(m_directWriteFontFace); + + m_fontEngineData->directWriteFactory->AddRef(); + m_directWriteFontFace->AddRef(); + + fontDef.pixelSize = pixelSize; + collectMetrics(); + cache_cost = (m_ascent.toInt() + m_descent.toInt()) * m_xHeight.toInt() * 2000; +} + +QWindowsFontEngineDirectWrite::~QWindowsFontEngineDirectWrite() +{ + qCDebug(lcQpaFonts) << __FUNCTION__; + + m_fontEngineData->directWriteFactory->Release(); + m_directWriteFontFace->Release(); + + if (m_directWriteBitmapRenderTarget != 0) + m_directWriteBitmapRenderTarget->Release(); + + if (!m_uniqueFamilyName.isEmpty()) { + QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase(); + static_cast<QWindowsFontDatabase *>(pfdb)->derefUniqueFont(m_uniqueFamilyName); + } +} + +#ifndef Q_CC_MINGW +typedef IDWriteLocalFontFileLoader QIdWriteLocalFontFileLoader; + +static UUID uuidIdWriteLocalFontFileLoader() +{ + return __uuidof(IDWriteLocalFontFileLoader); +} +#else // !Q_CC_MINGW +DECLARE_INTERFACE_(QIdWriteLocalFontFileLoader, IDWriteFontFileLoader) +{ + STDMETHOD(GetFilePathLengthFromKey)(THIS_ void const *, UINT32, UINT32*) PURE; + STDMETHOD(GetFilePathFromKey)(THIS_ void const *, UINT32, WCHAR *, UINT32) PURE; + STDMETHOD(GetLastWriteTimeFromKey)(THIS_ void const *, UINT32, FILETIME *) PURE; +}; + +static UUID uuidIdWriteLocalFontFileLoader() +{ + static const UUID result = { 0xb2d9f3ec, 0xc9fe, 0x4a11, {0xa2, 0xec, 0xd8, 0x62, 0x8, 0xf7, 0xc0, 0xa2}}; + return result; +} +#endif // Q_CC_MINGW + +QString QWindowsFontEngineDirectWrite::filenameFromFontFile(IDWriteFontFile *fontFile) +{ + IDWriteFontFileLoader *loader = Q_NULLPTR; + + HRESULT hr = fontFile->GetLoader(&loader); + if (FAILED(hr)) { + qErrnoWarning("%s: GetLoader failed", __FUNCTION__); + return QString(); + } + + QIdWriteLocalFontFileLoader *localLoader = Q_NULLPTR; + hr = loader->QueryInterface(uuidIdWriteLocalFontFileLoader(), + reinterpret_cast<void **>(&localLoader)); + + const void *fontFileReferenceKey = Q_NULLPTR; + UINT32 fontFileReferenceKeySize = 0; + if (SUCCEEDED(hr)) { + hr = fontFile->GetReferenceKey(&fontFileReferenceKey, + &fontFileReferenceKeySize); + if (FAILED(hr)) + qErrnoWarning(hr, "%s: GetReferenceKey failed", __FUNCTION__); + } + + UINT32 filePathLength = 0; + if (SUCCEEDED(hr)) { + hr = localLoader->GetFilePathLengthFromKey(fontFileReferenceKey, + fontFileReferenceKeySize, + &filePathLength); + if (FAILED(hr)) + qErrnoWarning(hr, "GetFilePathLength failed", __FUNCTION__); + } + + QString ret; + if (SUCCEEDED(hr) && filePathLength > 0) { + QVarLengthArray<wchar_t> filePath(filePathLength + 1); + + hr = localLoader->GetFilePathFromKey(fontFileReferenceKey, + fontFileReferenceKeySize, + filePath.data(), + filePathLength + 1); + if (FAILED(hr)) + qErrnoWarning(hr, "%s: GetFilePathFromKey failed", __FUNCTION__); + else + ret = QString::fromWCharArray(filePath.data()); + } + + if (localLoader != Q_NULLPTR) + localLoader->Release(); + + if (loader != Q_NULLPTR) + loader->Release(); + return ret; +} + +void QWindowsFontEngineDirectWrite::collectMetrics() +{ + DWRITE_FONT_METRICS metrics; + + m_directWriteFontFace->GetMetrics(&metrics); + m_unitsPerEm = metrics.designUnitsPerEm; + + m_lineThickness = DESIGN_TO_LOGICAL(metrics.underlineThickness); + m_ascent = DESIGN_TO_LOGICAL(metrics.ascent); + m_capHeight = DESIGN_TO_LOGICAL(metrics.capHeight); + m_descent = DESIGN_TO_LOGICAL(metrics.descent); + m_xHeight = DESIGN_TO_LOGICAL(metrics.xHeight); + m_lineGap = DESIGN_TO_LOGICAL(metrics.lineGap); + m_underlinePosition = DESIGN_TO_LOGICAL(metrics.underlinePosition); + + IDWriteFontFile *fontFile = Q_NULLPTR; + UINT32 numberOfFiles = 1; + if (SUCCEEDED(m_directWriteFontFace->GetFiles(&numberOfFiles, &fontFile))) { + m_faceId.filename = QFile::encodeName(filenameFromFontFile(fontFile)); + fontFile->Release(); + } +} + +QFixed QWindowsFontEngineDirectWrite::underlinePosition() const +{ + if (m_underlinePosition > 0) + return m_underlinePosition; + else + return QFontEngine::underlinePosition(); +} + +QFixed QWindowsFontEngineDirectWrite::lineThickness() const +{ + if (m_lineThickness > 0) + return m_lineThickness; + else + return QFontEngine::lineThickness(); +} + +bool QWindowsFontEngineDirectWrite::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + bool ret = false; + + const void *tableData = 0; + UINT32 tableSize; + void *tableContext = 0; + BOOL exists; + HRESULT hr = m_directWriteFontFace->TryGetFontTable(qbswap<quint32>(tag), + &tableData, &tableSize, + &tableContext, &exists); + if (SUCCEEDED(hr)) { + if (exists) { + ret = true; + if (buffer && *length >= tableSize) + memcpy(buffer, tableData, tableSize); + *length = tableSize; + Q_ASSERT(int(*length) > 0); + } + m_directWriteFontFace->ReleaseFontTable(tableContext); + } else { + qErrnoWarning("%s: TryGetFontTable failed", __FUNCTION__); + } + + return ret; +} + +QFixed QWindowsFontEngineDirectWrite::emSquareSize() const +{ + if (m_unitsPerEm > 0) + return m_unitsPerEm; + else + return QFontEngine::emSquareSize(); +} + +glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4) const +{ + UINT16 glyphIndex; + + HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(&ucs4, 1, &glyphIndex); + if (FAILED(hr)) { + qErrnoWarning("%s: glyphIndex failed", __FUNCTION__); + glyphIndex = 0; + } + + return glyphIndex; +} + +bool QWindowsFontEngineDirectWrite::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; + } + + QVarLengthArray<UINT32> codePoints(len); + int actualLength = 0; + QStringIterator it(str, str + len); + while (it.hasNext()) + codePoints[actualLength++] = it.next(); + + QVarLengthArray<UINT16> glyphIndices(actualLength); + HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), actualLength, + glyphIndices.data()); + if (FAILED(hr)) { + qErrnoWarning("%s: GetGlyphIndicesW failed", __FUNCTION__); + return false; + } + + for (int i = 0; i < actualLength; ++i) + glyphs->glyphs[i] = glyphIndices.at(i); + + *nglyphs = actualLength; + glyphs->numGlyphs = actualLength; + + if (!(flags & GlyphIndicesOnly)) + recalcAdvances(glyphs, 0); + + return true; +} + +QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId() const +{ + return m_faceId; +} + +void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const +{ + QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs); + + // ### Caching? + for(int i=0; i<glyphs->numGlyphs; i++) + glyphIndices[i] = UINT16(glyphs->glyphs[i]); + + QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size()); + HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(), + glyphIndices.size(), + glyphMetrics.data()); + if (SUCCEEDED(hr)) { + qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0; + for (int i = 0; i < glyphs->numGlyphs; ++i) + glyphs->advances[i] = DESIGN_TO_LOGICAL(glyphMetrics[i].advanceWidth * stretch); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < glyphs->numGlyphs; ++i) + glyphs->advances[i] = glyphs->advances[i].round(); + } + } else { + qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__); + } +} + +void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags) +{ + QVarLengthArray<UINT16> glyphIndices(nglyphs); + QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs); + QVarLengthArray<FLOAT> glyphAdvances(nglyphs); + + for (int i=0; i<nglyphs; ++i) { + glyphIndices[i] = glyphs[i]; + glyphOffsets[i].advanceOffset = positions[i].x.toReal(); + glyphOffsets[i].ascenderOffset = -positions[i].y.toReal(); + glyphAdvances[i] = 0.0; + } + + GeometrySink geometrySink(path); + HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline( + fontDef.pixelSize, + glyphIndices.data(), + glyphAdvances.data(), + glyphOffsets.data(), + nglyphs, + false, + flags & QTextItem::RightToLeft, + &geometrySink + ); + + if (FAILED(hr)) + qErrnoWarning("%s: GetGlyphRunOutline failed", __FUNCTION__); +} + +glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(const QGlyphLayout &glyphs) +{ + if (glyphs.numGlyphs == 0) + return glyph_metrics_t(); + + bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; + + QFixed w = 0; + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += round ? glyphs.effectiveAdvance(i).round() : glyphs.effectiveAdvance(i); + + } + + return glyph_metrics_t(0, -m_ascent, w - lastRightBearing(glyphs), m_ascent + m_descent, w, 0); +} + +glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g) +{ + UINT16 glyphIndex = g; + + DWRITE_GLYPH_METRICS glyphMetrics; + HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics); + if (SUCCEEDED(hr)) { + QFixed advanceWidth = DESIGN_TO_LOGICAL(glyphMetrics.advanceWidth); + QFixed leftSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.leftSideBearing); + QFixed rightSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.rightSideBearing); + QFixed advanceHeight = DESIGN_TO_LOGICAL(glyphMetrics.advanceHeight); + QFixed verticalOriginY = DESIGN_TO_LOGICAL(glyphMetrics.verticalOriginY); + QFixed topSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.topSideBearing); + QFixed bottomSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.bottomSideBearing); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + advanceWidth = advanceWidth.round(); + advanceHeight = advanceHeight.round(); + } + + QFixed width = advanceWidth - leftSideBearing - rightSideBearing; + QFixed height = advanceHeight - topSideBearing - bottomSideBearing; + return glyph_metrics_t(leftSideBearing, + -verticalOriginY + topSideBearing, + width, + height, + advanceWidth, + 0); + } else { + qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__); + } + + return glyph_metrics_t(); +} + +QFixed QWindowsFontEngineDirectWrite::ascent() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_ascent.round() + : m_ascent; +} + +QFixed QWindowsFontEngineDirectWrite::capHeight() const +{ + if (m_capHeight <= 0) + return calculatedCapHeight(); + + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_capHeight.round() + : m_capHeight; +} + +QFixed QWindowsFontEngineDirectWrite::descent() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_descent.round() + : m_descent; +} + +QFixed QWindowsFontEngineDirectWrite::leading() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_lineGap.round() + : m_lineGap; +} + +QFixed QWindowsFontEngineDirectWrite::xHeight() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_xHeight.round() + : m_xHeight; +} + +qreal QWindowsFontEngineDirectWrite::maxCharWidth() const +{ + // ### + return 0; +} + +QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) +{ + QImage im = alphaRGBMapForGlyph(glyph, subPixelPosition, t); + + QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8); + + for (int y=0; y<im.height(); ++y) { + const uint *src = reinterpret_cast<const uint *>(im.constScanLine(y)); + uchar *dst = alphaMap.scanLine(y); + for (int x=0; x<im.width(); ++x) { + *dst = 255 - (m_fontEngineData->pow_gamma[qGray(0xffffffff - *src)] * 255. / 2047.); + ++dst; + ++src; + } + } + + return alphaMap; +} + +QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) +{ + return alphaMapForGlyph(glyph, subPixelPosition, QTransform()); +} + +bool QWindowsFontEngineDirectWrite::supportsSubPixelPositions() const +{ + return true; +} + +QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, + QFixed subPixelPosition, + int margin, + const QTransform &originalTransform) +{ + UINT16 glyphIndex = t; + FLOAT glyphAdvance = 0; + + DWRITE_GLYPH_OFFSET glyphOffset; + glyphOffset.advanceOffset = 0; + glyphOffset.ascenderOffset = 0; + + DWRITE_GLYPH_RUN glyphRun; + glyphRun.fontFace = m_directWriteFontFace; + glyphRun.fontEmSize = fontDef.pixelSize; + glyphRun.glyphCount = 1; + glyphRun.glyphIndices = &glyphIndex; + glyphRun.glyphAdvances = &glyphAdvance; + glyphRun.isSideways = false; + glyphRun.bidiLevel = 0; + glyphRun.glyphOffsets = &glyphOffset; + + QTransform xform = originalTransform; + if (fontDef.stretch != 100) + xform.scale(fontDef.stretch / 100.0, 1.0); + + DWRITE_MATRIX transform; + transform.dx = subPixelPosition.toReal(); + transform.dy = 0; + transform.m11 = xform.m11(); + transform.m12 = xform.m12(); + transform.m21 = xform.m21(); + transform.m22 = xform.m22(); + + DWRITE_RENDERING_MODE renderMode = + hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference)); + + IDWriteGlyphRunAnalysis *glyphAnalysis = NULL; + HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis( + &glyphRun, + 1.0f, + &transform, + renderMode, + DWRITE_MEASURING_MODE_NATURAL, + 0.0, 0.0, + &glyphAnalysis + ); + + if (SUCCEEDED(hr)) { + RECT rect; + glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect); + + QRect boundingRect = QRect(QPoint(rect.left - margin, + rect.top - margin), + QPoint(rect.right + margin, + rect.bottom + margin)); + + + const int width = boundingRect.width() - 1; // -1 due to Qt's off-by-one definition of a QRect + const int height = boundingRect.height() - 1; + + QImage image; +#if defined(QT_USE_DIRECTWRITE2) + HRESULT hr = DWRITE_E_NOCOLOR; + IDWriteColorGlyphRunEnumerator *enumerator = 0; + IDWriteFactory2 *factory2 = Q_NULLPTR; + if (glyphFormat == QFontEngine::Format_ARGB + && SUCCEEDED(m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2), + reinterpret_cast<void **>(&factory2)))) { + hr = factory2->TranslateColorGlyphRun(0.0f, + 0.0f, + &glyphRun, + NULL, + DWRITE_MEASURING_MODE_NATURAL, + NULL, + 0, + &enumerator); + image = QImage(width, height, QImage::Format_ARGB32_Premultiplied); + image.fill(0); + } else +#endif + { + image = QImage(width, height, QImage::Format_RGB32); + image.fill(0xffffffff); + } + +#if defined(QT_USE_DIRECTWRITE2) + BOOL ok = true; + if (SUCCEEDED(hr)) { + while (SUCCEEDED(hr) && ok) { + const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun = 0; + hr = enumerator->GetCurrentRun(&colorGlyphRun); + if (FAILED(hr)) { // No colored runs, only outline + qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed", __FUNCTION__); + break; + } + + IDWriteGlyphRunAnalysis *colorGlyphsAnalysis = NULL; + hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis( + &colorGlyphRun->glyphRun, + 1.0f, + &transform, + renderMode, + DWRITE_MEASURING_MODE_NATURAL, + 0.0, 0.0, + &colorGlyphsAnalysis + ); + if (FAILED(hr)) { + qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed for color run", __FUNCTION__); + break; + } + + float r = qBound(0.0f, colorGlyphRun->runColor.r, 1.0f); + float g = qBound(0.0f, colorGlyphRun->runColor.g, 1.0f); + float b = qBound(0.0f, colorGlyphRun->runColor.b, 1.0f); + float a = qBound(0.0f, colorGlyphRun->runColor.a, 1.0f); + + if (!qFuzzyIsNull(a)) { + renderGlyphRun(&image, + r, + g, + b, + a, + colorGlyphsAnalysis, + boundingRect); + } + colorGlyphsAnalysis->Release(); + + hr = enumerator->MoveNext(&ok); + if (FAILED(hr)) { + qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::MoveNext failed", __FUNCTION__); + break; + } + } + } else +#endif + { + renderGlyphRun(&image, + 0.0, + 0.0, + 0.0, + 1.0, + glyphAnalysis, + boundingRect); + } + + glyphAnalysis->Release(); + return image; + } else { + qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed", __FUNCTION__); + return QImage(); + } +} + + +void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination, + float r, + float g, + float b, + float a, + IDWriteGlyphRunAnalysis *glyphAnalysis, + const QRect &boundingRect) +{ + const int width = destination->width(); + const int height = destination->height(); + + r *= 255.0; + g *= 255.0; + b *= 255.0; + + const int size = width * height * 3; + if (size > 0) { + RECT rect; + rect.left = boundingRect.left(); + rect.top = boundingRect.top(); + rect.right = boundingRect.right(); + rect.bottom = boundingRect.bottom(); + + QVarLengthArray<BYTE, 1024> alphaValueArray(size); + BYTE *alphaValues = alphaValueArray.data(); + memset(alphaValues, 0, size); + + HRESULT hr = glyphAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, + &rect, + alphaValues, + size); + if (SUCCEEDED(hr)) { + if (destination->hasAlphaChannel()) { + for (int y = 0; y < height; ++y) { + uint *dest = reinterpret_cast<uint *>(destination->scanLine(y)); + BYTE *src = alphaValues + width * 3 * y; + + for (int x = 0; x < width; ++x) { + float redAlpha = a * *src++ / 255.0; + float greenAlpha = a * *src++ / 255.0; + float blueAlpha = a * *src++ / 255.0; + float averageAlpha = (redAlpha + greenAlpha + blueAlpha) / 3.0; + + QRgb currentRgb = dest[x]; + dest[x] = qRgba(qRound(qRed(currentRgb) * (1.0 - averageAlpha) + averageAlpha * r), + qRound(qGreen(currentRgb) * (1.0 - averageAlpha) + averageAlpha * g), + qRound(qBlue(currentRgb) * (1.0 - averageAlpha) + averageAlpha * b), + qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255)); + } + } + + } else { + for (int y = 0; y < height; ++y) { + uint *dest = reinterpret_cast<uint *>(destination->scanLine(y)); + BYTE *src = alphaValues + width * 3 * y; + + for (int x = 0; x < width; ++x) { + dest[x] = *(src + 0) << 16 + | *(src + 1) << 8 + | *(src + 2); + + src += 3; + } + } + } + } else { + qErrnoWarning("%s: CreateAlphaTexture failed", __FUNCTION__); + } + } else { + glyphAnalysis->Release(); + qWarning("%s: Glyph has no bounds", __FUNCTION__); + } +} + +QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t, + QFixed subPixelPosition, + const QTransform &xform) +{ + QImage mask = imageForGlyph(t, + subPixelPosition, + glyphMargin(QFontEngine::Format_A32), + xform); + + return mask.depth() == 32 + ? mask + : mask.convertToFormat(QImage::Format_RGB32); +} + +QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize) const +{ + QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(m_directWriteFontFace, + pixelSize, + m_fontEngineData); + + fontEngine->fontDef = fontDef; + fontEngine->fontDef.pixelSize = pixelSize; + if (!m_uniqueFamilyName.isEmpty()) { + fontEngine->setUniqueFamilyName(m_uniqueFamilyName); + QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase(); + static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(m_uniqueFamilyName); + } + + return fontEngine; +} + +Qt::HANDLE QWindowsFontEngineDirectWrite::handle() const +{ + return m_directWriteFontFace; +} + +void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request, + int dpi) +{ + fontDef = request; + + if (fontDef.pointSize < 0) + fontDef.pointSize = fontDef.pixelSize * 72. / dpi; + else if (fontDef.pixelSize == -1) + fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.); +} + +QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName) +{ + const wchar_t key[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes"; + const QString substitute = + QWindowsFontDatabase::readRegistryString(HKEY_LOCAL_MACHINE, key, + reinterpret_cast<const wchar_t *>(familyName.utf16())); + return substitute.isEmpty() ? familyName : substitute; +} + +glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph, + QFixed subPixelPosition, + const QTransform &originalTransform, + GlyphFormat format) +{ + Q_UNUSED(format); + + QTransform matrix = originalTransform; + if (fontDef.stretch != 100) + matrix.scale(fontDef.stretch / 100.0, 1.0); + + glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix); // To get transformed advance + + UINT16 glyphIndex = glyph; + FLOAT glyphAdvance = 0; + + DWRITE_GLYPH_OFFSET glyphOffset; + glyphOffset.advanceOffset = 0; + glyphOffset.ascenderOffset = 0; + + DWRITE_GLYPH_RUN glyphRun; + glyphRun.fontFace = m_directWriteFontFace; + glyphRun.fontEmSize = fontDef.pixelSize; + glyphRun.glyphCount = 1; + glyphRun.glyphIndices = &glyphIndex; + glyphRun.glyphAdvances = &glyphAdvance; + glyphRun.isSideways = false; + glyphRun.bidiLevel = 0; + glyphRun.glyphOffsets = &glyphOffset; + + DWRITE_MATRIX transform; + transform.dx = subPixelPosition.toReal(); + transform.dy = 0; + transform.m11 = matrix.m11(); + transform.m12 = matrix.m12(); + transform.m21 = matrix.m21(); + transform.m22 = matrix.m22(); + + DWRITE_RENDERING_MODE renderMode = + hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference)); + + IDWriteGlyphRunAnalysis *glyphAnalysis = NULL; + HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis( + &glyphRun, + 1.0f, + &transform, + renderMode, + DWRITE_MEASURING_MODE_NATURAL, + 0.0, 0.0, + &glyphAnalysis + ); + + if (SUCCEEDED(hr)) { + RECT rect; + glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect); + glyphAnalysis->Release(); + + int margin = glyphMargin(QFontEngine::Format_A32); + + return glyph_metrics_t(rect.left, + rect.top, + rect.right - rect.left + margin * 2, + rect.bottom - rect.top + margin * 2, + bbox.xoff, bbox.yoff); + } else { + return glyph_metrics_t(); + } +} + +QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) +{ + return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_A32), t); +} + +QT_END_NAMESPACE + +#endif // QT_NO_DIRECTWRITE diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite_p.h b/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite_p.h new file mode 100644 index 0000000000..65b16b9ba7 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** 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 QWINDOWSFONTENGINEDIRECTWRITE_H +#define QWINDOWSFONTENGINEDIRECTWRITE_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 <QtCore/qglobal.h> + +#ifndef QT_NO_DIRECTWRITE + +#include <QtGui/private/qfontengine_p.h> +#include <QtCore/QSharedPointer> + +struct IDWriteFont; +struct IDWriteFontFace; +struct IDWriteFontFile; +struct IDWriteFactory; +struct IDWriteBitmapRenderTarget; +struct IDWriteGdiInterop; +struct IDWriteGlyphRunAnalysis; + +QT_BEGIN_NAMESPACE + +class QWindowsFontEngineData; + +class QWindowsFontEngineDirectWrite : public QFontEngine +{ +public: + explicit QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace, + qreal pixelSize, + const QSharedPointer<QWindowsFontEngineData> &d); + ~QWindowsFontEngineDirectWrite(); + + void initFontInfo(const QFontDef &request, int dpi); + + QFixed lineThickness() const Q_DECL_OVERRIDE; + QFixed underlinePosition() const Q_DECL_OVERRIDE; + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const Q_DECL_OVERRIDE; + QFixed emSquareSize() const Q_DECL_OVERRIDE; + + glyph_t glyphIndex(uint ucs4) const Q_DECL_OVERRIDE; + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, + ShaperFlags flags) const Q_DECL_OVERRIDE; + void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags) const Q_DECL_OVERRIDE; + + void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags) Q_DECL_OVERRIDE; + + glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) Q_DECL_OVERRIDE; + glyph_metrics_t boundingBox(glyph_t g) Q_DECL_OVERRIDE; + glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed, + const QTransform &matrix, GlyphFormat) 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; + qreal maxCharWidth() const Q_DECL_OVERRIDE; + FaceId faceId() const Q_DECL_OVERRIDE; + + bool supportsSubPixelPositions() const Q_DECL_OVERRIDE; + + QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) Q_DECL_OVERRIDE; + QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; + QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, const QTransform &xform) Q_DECL_OVERRIDE; + QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; + + QFontEngine *cloneWithSize(qreal pixelSize) const Q_DECL_OVERRIDE; + Qt::HANDLE handle() const Q_DECL_OVERRIDE; + + const QSharedPointer<QWindowsFontEngineData> &fontEngineData() const { return m_fontEngineData; } + + static QString fontNameSubstitute(const QString &familyName); + + IDWriteFontFace *directWriteFontFace() const { return m_directWriteFontFace; } + + void setUniqueFamilyName(const QString &newName) { m_uniqueFamilyName = newName; } + +private: + QImage imageForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform); + void collectMetrics(); + void renderGlyphRun(QImage *destination, float r, float g, float b, float a, IDWriteGlyphRunAnalysis *glyphAnalysis, const QRect &boundingRect); + static QString filenameFromFontFile(IDWriteFontFile *fontFile); + + const QSharedPointer<QWindowsFontEngineData> m_fontEngineData; + + IDWriteFontFace *m_directWriteFontFace; + IDWriteBitmapRenderTarget *m_directWriteBitmapRenderTarget; + + QFixed m_lineThickness; + QFixed m_underlinePosition; + int m_unitsPerEm; + QFixed m_ascent; + QFixed m_capHeight; + QFixed m_descent; + QFixed m_xHeight; + QFixed m_lineGap; + FaceId m_faceId; + QString m_uniqueFamilyName; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_DIRECTWRITE + +#endif // QWINDOWSFONTENGINEDIRECTWRITE_H diff --git a/src/platformsupport/fontdatabases/windows/qwindowsnativeimage.cpp b/src/platformsupport/fontdatabases/windows/qwindowsnativeimage.cpp new file mode 100644 index 0000000000..7022615511 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsnativeimage.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** 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 "qwindowsnativeimage_p.h" + +#include <QtGui/private/qpaintengine_p.h> +#include <QtGui/private/qpaintengine_raster_p.h> + +QT_BEGIN_NAMESPACE + +typedef struct { + BITMAPINFOHEADER bmiHeader; + DWORD redMask; + DWORD greenMask; + DWORD blueMask; +} BITMAPINFO_MASK; + +/*! + \class QWindowsNativeImage + \brief Windows Native image + + Note that size can be 0 (widget autotests with zero size), which + causes CreateDIBSection() to fail. + + \sa QWindowsBackingStore + \internal + \ingroup qt-lighthouse-win +*/ + +static inline HDC createDC() +{ + HDC display_dc = GetDC(0); + HDC hdc = CreateCompatibleDC(display_dc); + ReleaseDC(0, display_dc); + Q_ASSERT(hdc); + return hdc; +} + +static inline HBITMAP createDIB(HDC hdc, int width, int height, + QImage::Format format, + uchar **bitsIn) +{ + BITMAPINFO_MASK bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -height; // top-down. + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biSizeImage = 0; + + if (format == QImage::Format_RGB16) { + bmi.bmiHeader.biBitCount = 16; + bmi.bmiHeader.biCompression = BI_BITFIELDS; + bmi.redMask = 0xF800; + bmi.greenMask = 0x07E0; + bmi.blueMask = 0x001F; + } else { + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.redMask = 0; + bmi.greenMask = 0; + bmi.blueMask = 0; + } + + uchar *bits = Q_NULLPTR; + HBITMAP bitmap = CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO *>(&bmi), + DIB_RGB_COLORS, reinterpret_cast<void **>(&bits), 0, 0); + if (Q_UNLIKELY(!bitmap || !bits)) + qFatal("%s: CreateDIBSection failed.", __FUNCTION__); + + *bitsIn = bits; + return bitmap; +} + +QWindowsNativeImage::QWindowsNativeImage(int width, int height, + QImage::Format format) : + m_hdc(createDC()), + m_bitmap(0), + m_null_bitmap(0) +{ + if (width != 0 && height != 0) { + uchar *bits; + m_bitmap = createDIB(m_hdc, width, height, format, &bits); + m_null_bitmap = static_cast<HBITMAP>(SelectObject(m_hdc, m_bitmap)); + m_image = QImage(bits, width, height, format); + Q_ASSERT(m_image.paintEngine()->type() == QPaintEngine::Raster); + static_cast<QRasterPaintEngine *>(m_image.paintEngine())->setDC(m_hdc); + } else { + m_image = QImage(width, height, format); + } + + GdiFlush(); +} + +QWindowsNativeImage::~QWindowsNativeImage() +{ + if (m_hdc) { + if (m_bitmap) { + if (m_null_bitmap) + SelectObject(m_hdc, m_null_bitmap); + DeleteObject(m_bitmap); + } + DeleteDC(m_hdc); + } +} + +QImage::Format QWindowsNativeImage::systemFormat() +{ + static int depth = -1; + if (depth == -1) { + if (HDC defaultDC = GetDC(0)) { + depth = GetDeviceCaps(defaultDC, BITSPIXEL); + ReleaseDC(0, defaultDC); + } else { + // FIXME Same remark as in QWindowsFontDatabase::defaultVerticalDPI() + // BONUS FIXME: Is 32 too generous/optimistic? + depth = 32; + } + } + return depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/windows/qwindowsnativeimage_p.h b/src/platformsupport/fontdatabases/windows/qwindowsnativeimage_p.h new file mode 100644 index 0000000000..c27c0d1e98 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsnativeimage_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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 QWINDOWSNATIVEIMAGE_H +#define QWINDOWSNATIVEIMAGE_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 <QtCore/QtGlobal> +#include <QtCore/qt_windows.h> +#include <QtGui/QImage> + +QT_BEGIN_NAMESPACE + +class QWindowsNativeImage +{ + Q_DISABLE_COPY(QWindowsNativeImage) +public: + QWindowsNativeImage(int width, int height, + QImage::Format format); + + ~QWindowsNativeImage(); + + inline int width() const { return m_image.width(); } + inline int height() const { return m_image.height(); } + + QImage &image() { return m_image; } + const QImage &image() const { return m_image; } + + HDC hdc() const { return m_hdc; } + + static QImage::Format systemFormat(); + +private: + const HDC m_hdc; + QImage m_image; + + HBITMAP m_bitmap; + HBITMAP m_null_bitmap; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSNATIVEIMAGE_H diff --git a/src/platformsupport/fontdatabases/windows/windows.pri b/src/platformsupport/fontdatabases/windows/windows.pri new file mode 100644 index 0000000000..419c4dc6d9 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/windows.pri @@ -0,0 +1,33 @@ +QT *= gui-private + +SOURCES += \ + $$PWD/qwindowsfontdatabase.cpp \ + $$PWD/qwindowsfontengine.cpp \ + $$PWD/qwindowsnativeimage.cpp + +HEADERS += \ + $$PWD/qwindowsfontdatabase_p.h \ + $$PWD/qwindowsfontengine_p.h \ + $$PWD/qwindowsnativeimage_p.h + +qtConfig(freetype) { + SOURCES += $$PWD/qwindowsfontdatabase_ft.cpp + HEADERS += $$PWD/qwindowsfontdatabase_ft_p.h + qtConfig(system-freetype) { + include($$QT_SOURCE_TREE/src/platformsupport/fontdatabases/basic/basic.pri) + } else { + include($$QT_SOURCE_TREE/src/3rdparty/freetype_dependency.pri) + } +} + +qtConfig(directwrite) { + qtConfig(directwrite2): \ + DEFINES *= QT_USE_DIRECTWRITE2 + + SOURCES += $$PWD/qwindowsfontenginedirectwrite.cpp + HEADERS += $$PWD/qwindowsfontenginedirectwrite_p.h +} else { + DEFINES *= QT_NO_DIRECTWRITE +} + +LIBS += -lole32 -lgdi32 -luser32 diff --git a/src/platformsupport/glxconvenience/glxconvenience.pri b/src/platformsupport/glxconvenience/glxconvenience.pri deleted file mode 100644 index 80e79ee663..0000000000 --- a/src/platformsupport/glxconvenience/glxconvenience.pri +++ /dev/null @@ -1,8 +0,0 @@ -qtConfig(xlib) { - qtConfig(opengl):!qtConfig(opengles2) { - qtConfig(xrender): QMAKE_USE_PRIVATE += xrender - LIBS_PRIVATE += $$QMAKE_LIBS_X11 - HEADERS += $$PWD/qglxconvenience_p.h - SOURCES += $$PWD/qglxconvenience.cpp - } -} diff --git a/src/platformsupport/glxconvenience/glxconvenience.pro b/src/platformsupport/glxconvenience/glxconvenience.pro new file mode 100644 index 0000000000..185d6b0364 --- /dev/null +++ b/src/platformsupport/glxconvenience/glxconvenience.pro @@ -0,0 +1,16 @@ +TARGET = QtGlxSupport +MODULE = glx_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +qtConfig(xrender): QMAKE_USE_PRIVATE += xrender +LIBS_PRIVATE += $$QMAKE_LIBS_X11 + +HEADERS += qglxconvenience_p.h +SOURCES += qglxconvenience.cpp + +load(qt_module) diff --git a/src/platformsupport/glxconvenience/qglxconvenience.cpp b/src/platformsupport/glxconvenience/qglxconvenience.cpp index 7dc29fae6d..4225bebf37 100644 --- a/src/platformsupport/glxconvenience/qglxconvenience.cpp +++ b/src/platformsupport/glxconvenience/qglxconvenience.cpp @@ -386,5 +386,10 @@ bool qglx_reduceFormat(QSurfaceFormat *format) return true; } + if (format->stereo()) { + format->setStereo(false); + return true; + } + return false; } diff --git a/src/platformsupport/graphics/graphics.pri b/src/platformsupport/graphics/graphics.pri deleted file mode 100644 index 43062682aa..0000000000 --- a/src/platformsupport/graphics/graphics.pri +++ /dev/null @@ -1,2 +0,0 @@ -HEADERS += $$PWD/qrasterbackingstore_p.h -SOURCES += $$PWD/qrasterbackingstore.cpp diff --git a/src/platformsupport/graphics/graphics.pro b/src/platformsupport/graphics/graphics.pro new file mode 100644 index 0000000000..878fca7f49 --- /dev/null +++ b/src/platformsupport/graphics/graphics.pro @@ -0,0 +1,13 @@ +TARGET = QtGraphicsSupport +MODULE = graphics_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +HEADERS += $$PWD/qrasterbackingstore_p.h +SOURCES += $$PWD/qrasterbackingstore.cpp + +load(qt_module) diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h index 01f23f2542..d2e34fead3 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h @@ -53,7 +53,7 @@ #include "qevdevkeyboardhandler_p.h" -#include <QtPlatformSupport/private/qdevicediscovery_p.h> +#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h> #include <QObject> #include <QHash> diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp index 2d96691b09..b2f3fe5787 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp +++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp @@ -44,7 +44,7 @@ #include <QScreen> #include <QLoggingCategory> #include <qpa/qwindowsysteminterface.h> -#include <QtPlatformSupport/private/qdevicediscovery_p.h> +#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h> #include <private/qguiapplication_p.h> #include <private/qinputdevicemanager_p_p.h> #include <private/qhighdpiscaling_p.h> diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp index 89a60df98d..4b00424e92 100644 --- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp @@ -43,7 +43,7 @@ #include <QStringList> #include <QGuiApplication> #include <QLoggingCategory> -#include <QtPlatformSupport/private/qdevicediscovery_p.h> +#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h> #include <private/qguiapplication_p.h> #include <private/qinputdevicemanager_p_p.h> diff --git a/src/platformsupport/input/evdevtouch/evdevtouch.pri b/src/platformsupport/input/evdevtouch/evdevtouch.pri index 58fafcd8f9..0ad236e882 100644 --- a/src/platformsupport/input/evdevtouch/evdevtouch.pri +++ b/src/platformsupport/input/evdevtouch/evdevtouch.pri @@ -12,6 +12,5 @@ qtConfig(libudev): \ QMAKE_USE_PRIVATE += libudev qtConfig(mtdev) { - CONFIG += link_pkgconfig - PKGCONFIG_PRIVATE += mtdev + QMAKE_USE_PRIVATE += mtdev } diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp index d0c6c10224..ab71d08fb1 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp @@ -43,7 +43,7 @@ #include <QStringList> #include <QGuiApplication> #include <QLoggingCategory> -#include <QtPlatformSupport/private/qdevicediscovery_p.h> +#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h> #include <private/qguiapplication_p.h> #include <private/qinputdevicemanager_p_p.h> diff --git a/src/platformsupport/input/input.pri b/src/platformsupport/input/input.pro index eb4d8a983d..2c2ace6780 100644 --- a/src/platformsupport/input/input.pri +++ b/src/platformsupport/input/input.pro @@ -1,3 +1,12 @@ +TARGET = QtInputSupport +MODULE = input_support + +QT = core-private gui-private devicediscovery_support-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + qtConfig(evdev) { include($$PWD/evdevmouse/evdevmouse.pri) include($$PWD/evdevkeyboard/evdevkeyboard.pri) @@ -16,3 +25,5 @@ qtConfig(libinput) { qtConfig(evdev)|qtConfig(libinput) { include($$PWD/shared/shared.pri) } + +load(qt_module) diff --git a/src/platformsupport/linuxaccessibility/application_p.h b/src/platformsupport/linuxaccessibility/application_p.h index 2e6d7a78c1..9c053b253c 100644 --- a/src/platformsupport/linuxaccessibility/application_p.h +++ b/src/platformsupport/linuxaccessibility/application_p.h @@ -51,12 +51,13 @@ // We mean it. // +#include <QtGui/private/qtguiglobal_p.h> #include <QtCore/QPointer> #include <QtCore/QQueue> #include <QtDBus/QDBusConnection> #include <QtGui/QAccessibleInterface> -#ifndef QT_NO_ACCESSIBILITY +QT_REQUIRE_CONFIG(accessibility); QT_BEGIN_NAMESPACE @@ -94,6 +95,4 @@ private: QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY - #endif diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp index f6c126a771..70c4aa563c 100644 --- a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +++ b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp @@ -50,7 +50,7 @@ #ifndef QT_NO_ACCESSIBILITY #include "socket_interface.h" #include "constant_mappings_p.h" -#include "../accessibility/qaccessiblebridgeutils_p.h" +#include <QtAccessibilitySupport/private/qaccessiblebridgeutils_p.h> #include "application_p.h" /*! diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor_p.h b/src/platformsupport/linuxaccessibility/atspiadaptor_p.h index 87bdbe4bb6..b5704f53ad 100644 --- a/src/platformsupport/linuxaccessibility/atspiadaptor_p.h +++ b/src/platformsupport/linuxaccessibility/atspiadaptor_p.h @@ -54,6 +54,7 @@ #include <atspi/atspi-constants.h> +#include <QtGui/private/qtguiglobal_p.h> #include <QtCore/qsharedpointer.h> #include <QtDBus/qdbusvirtualobject.h> #include <QtGui/qaccessible.h> @@ -61,7 +62,8 @@ #include "dbusconnection_p.h" #include "struct_marshallers_p.h" -#ifndef QT_NO_ACCESSIBILITY +QT_REQUIRE_CONFIG(accessibility); + QT_BEGIN_NAMESPACE class QAccessibleInterface; @@ -222,6 +224,5 @@ private: }; QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY #endif diff --git a/src/platformsupport/linuxaccessibility/bridge_p.h b/src/platformsupport/linuxaccessibility/bridge_p.h index 44dfa82f5d..1e435ca351 100644 --- a/src/platformsupport/linuxaccessibility/bridge_p.h +++ b/src/platformsupport/linuxaccessibility/bridge_p.h @@ -52,12 +52,14 @@ // We mean it. // +#include <QtGui/private/qtguiglobal_p.h> #include <QtDBus/qdbusconnection.h> #include <qpa/qplatformaccessibility.h> class DeviceEventControllerAdaptor; -#ifndef QT_NO_ACCESSIBILITY +QT_REQUIRE_CONFIG(accessibility); + QT_BEGIN_NAMESPACE class DBusConnection; @@ -89,6 +91,5 @@ private: }; QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY #endif diff --git a/src/platformsupport/linuxaccessibility/cache_p.h b/src/platformsupport/linuxaccessibility/cache_p.h index f141785301..e8529b779b 100644 --- a/src/platformsupport/linuxaccessibility/cache_p.h +++ b/src/platformsupport/linuxaccessibility/cache_p.h @@ -52,10 +52,12 @@ // We mean it. // +#include <QtGui/private/qtguiglobal_p.h> #include <QtCore/QObject> #include "struct_marshallers_p.h" -#ifndef QT_NO_ACCESSIBILITY +QT_REQUIRE_CONFIG(accessibility); + QT_BEGIN_NAMESPACE class QSpiDBusCache : public QObject @@ -76,6 +78,5 @@ public Q_SLOTS: }; QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY #endif /* Q_SPI_CACHE_H */ diff --git a/src/platformsupport/linuxaccessibility/constant_mappings_p.h b/src/platformsupport/linuxaccessibility/constant_mappings_p.h index a096261da6..4da818c8c1 100644 --- a/src/platformsupport/linuxaccessibility/constant_mappings_p.h +++ b/src/platformsupport/linuxaccessibility/constant_mappings_p.h @@ -58,10 +58,11 @@ #include "struct_marshallers_p.h" +#include <QtGui/private/qtguiglobal_p.h> #include <QtGui/QAccessible> #include <atspi/atspi-constants.h> -#ifndef QT_NO_ACCESSIBILITY +QT_REQUIRE_CONFIG(accessibility); // interface names from at-spi2-core/atspi/atspi-misc-private.h #define ATSPI_DBUS_NAME_REGISTRY "org.a11y.atspi.Registry" @@ -141,6 +142,5 @@ QSpiUIntList spiStateSetFromSpiStates(quint64 states); AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation); QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY #endif /* Q_SPI_CONSTANT_MAPPINGS_H */ diff --git a/src/platformsupport/linuxaccessibility/linuxaccessibility.pri b/src/platformsupport/linuxaccessibility/linuxaccessibility.pri deleted file mode 100644 index 1f36b815cf..0000000000 --- a/src/platformsupport/linuxaccessibility/linuxaccessibility.pri +++ /dev/null @@ -1,25 +0,0 @@ -qtConfig(accessibility-atspi-bridge) { - - QT_FOR_PRIVATE += dbus - include(../../3rdparty/atspi2/atspi2.pri) - - INCLUDEPATH += $$PWD - - HEADERS += \ - $$PWD/application_p.h \ - $$PWD/bridge_p.h \ - $$PWD/cache_p.h \ - $$PWD/struct_marshallers_p.h \ - $$PWD/constant_mappings_p.h \ - $$PWD/dbusconnection_p.h \ - $$PWD/atspiadaptor_p.h - - SOURCES += \ - $$PWD/application.cpp \ - $$PWD/bridge.cpp \ - $$PWD/cache.cpp \ - $$PWD/struct_marshallers.cpp \ - $$PWD/constant_mappings.cpp \ - $$PWD/dbusconnection.cpp \ - $$PWD/atspiadaptor.cpp -} diff --git a/src/platformsupport/linuxaccessibility/linuxaccessibility.pro b/src/platformsupport/linuxaccessibility/linuxaccessibility.pro new file mode 100644 index 0000000000..6d68909047 --- /dev/null +++ b/src/platformsupport/linuxaccessibility/linuxaccessibility.pro @@ -0,0 +1,30 @@ +TARGET = QtLinuxAccessibilitySupport +MODULE = linuxaccessibility_support + +QT = core-private dbus gui-private accessibility_support-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +include(../../3rdparty/atspi2/atspi2.pri) + +HEADERS += \ + application_p.h \ + bridge_p.h \ + cache_p.h \ + struct_marshallers_p.h \ + constant_mappings_p.h \ + dbusconnection_p.h \ + atspiadaptor_p.h + +SOURCES += \ + application.cpp \ + bridge.cpp \ + cache.cpp \ + struct_marshallers.cpp \ + constant_mappings.cpp \ + dbusconnection.cpp \ + atspiadaptor.cpp + +load(qt_module) diff --git a/src/platformsupport/linuxaccessibility/struct_marshallers_p.h b/src/platformsupport/linuxaccessibility/struct_marshallers_p.h index 28d96ec5ea..c8cc05ab5b 100644 --- a/src/platformsupport/linuxaccessibility/struct_marshallers_p.h +++ b/src/platformsupport/linuxaccessibility/struct_marshallers_p.h @@ -52,13 +52,15 @@ // We mean it. // +#include <QtGui/private/qtguiglobal_p.h> #include <QtCore/qvector.h> #include <QtCore/qpair.h> #include <QtDBus/QDBusArgument> #include <QtDBus/QDBusConnection> #include <QtDBus/QDBusObjectPath> -#ifndef QT_NO_ACCESSIBILITY +QT_REQUIRE_CONFIG(accessibility); + QT_BEGIN_NAMESPACE typedef QVector<int> QSpiIntList; @@ -192,5 +194,4 @@ Q_DECLARE_METATYPE(QSpiAttributeSet) Q_DECLARE_METATYPE(QSpiAppUpdate) Q_DECLARE_METATYPE(QSpiDeviceEvent) -#endif //QT_NO_ACCESSIBILITY #endif /* Q_SPI_STRUCT_MARSHALLERS_H */ diff --git a/src/platformsupport/platformcompositor/platformcompositor.pri b/src/platformsupport/platformcompositor/platformcompositor.pri deleted file mode 100644 index 1e908c9998..0000000000 --- a/src/platformsupport/platformcompositor/platformcompositor.pri +++ /dev/null @@ -1,7 +0,0 @@ -qtConfig(opengl) { - SOURCES += $$PWD/qopenglcompositor.cpp \ - $$PWD/qopenglcompositorbackingstore.cpp - - HEADERS += $$PWD/qopenglcompositor_p.h \ - $$PWD/qopenglcompositorbackingstore_p.h -} diff --git a/src/platformsupport/platformcompositor/platformcompositor.pro b/src/platformsupport/platformcompositor/platformcompositor.pro new file mode 100644 index 0000000000..633e71fb9d --- /dev/null +++ b/src/platformsupport/platformcompositor/platformcompositor.pro @@ -0,0 +1,18 @@ +TARGET = QtPlatformCompositorSupport +MODULE = platformcompositor_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +SOURCES += \ + qopenglcompositor.cpp \ + qopenglcompositorbackingstore.cpp + +HEADERS += \ + qopenglcompositor_p.h \ + qopenglcompositorbackingstore_p.h + +load(qt_module) diff --git a/src/platformsupport/platformsupport.pro b/src/platformsupport/platformsupport.pro index f9b57acaa8..b83038dbf9 100644 --- a/src/platformsupport/platformsupport.pro +++ b/src/platformsupport/platformsupport.pro @@ -1,27 +1,42 @@ -TARGET = QtPlatformSupport -QT = core-private gui-private +TEMPLATE = subdirs +QT_FOR_CONFIG += gui-private -CONFIG += static internal_module -mac:LIBS_PRIVATE += -lz +SUBDIRS = \ + eventdispatchers \ + devicediscovery \ + fbconvenience \ + themes -DEFINES += QT_NO_CAST_FROM_ASCII -PRECOMPILED_HEADER = ../corelib/global/qt_pch.h +qtConfig(freetype)|darwin|win32: \ + SUBDIRS += fontdatabases -include(cglconvenience/cglconvenience.pri) -include(eglconvenience/eglconvenience.pri) -include(eventdispatchers/eventdispatchers.pri) -include(fbconvenience/fbconvenience.pri) -include(fontdatabases/fontdatabases.pri) -include(glxconvenience/glxconvenience.pri) -include(input/input.pri) -include(devicediscovery/devicediscovery.pri) -include(services/services.pri) -include(themes/themes.pri) -include(accessibility/accessibility.pri) -include(linuxaccessibility/linuxaccessibility.pri) -include(clipboard/clipboard.pri) -include(platformcompositor/platformcompositor.pri) +qtConfig(evdev)|qtConfig(tslib)|qtConfig(libinput) { + SUBDIRS += input + input.depends += devicediscovery +} -darwin: include(graphics/graphics.pri) +unix:!darwin: \ + SUBDIRS += services -load(qt_module) +qtConfig(opengl): \ + SUBDIRS += platformcompositor +qtConfig(egl): \ + SUBDIRS += eglconvenience +qtConfig(xlib):qtConfig(opengl):!qtConfig(opengles2): \ + SUBDIRS += glxconvenience + +qtConfig(accessibility) { + SUBDIRS += accessibility + qtConfig(accessibility-atspi-bridge) { + SUBDIRS += linuxaccessibility + linuxaccessibility.depends += accessibility + } +} + +darwin { + SUBDIRS += \ + clipboard \ + graphics + macos: \ + SUBDIRS += cglconvenience +} diff --git a/src/platformsupport/services/services.pri b/src/platformsupport/services/services.pri deleted file mode 100644 index adee852626..0000000000 --- a/src/platformsupport/services/services.pri +++ /dev/null @@ -1,3 +0,0 @@ -unix:!mac { - include($$PWD/genericunix/genericunix.pri) -} diff --git a/src/platformsupport/services/services.pro b/src/platformsupport/services/services.pro new file mode 100644 index 0000000000..91957a0a78 --- /dev/null +++ b/src/platformsupport/services/services.pro @@ -0,0 +1,13 @@ +TARGET = QtServiceSupport +MODULE = service_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +unix:!darwin: \ + include($$PWD/genericunix/genericunix.pri) + +load(qt_module) diff --git a/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon_p.h b/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon_p.h index a383ef86fc..234ff60584 100644 --- a/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon_p.h +++ b/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon_p.h @@ -52,9 +52,9 @@ // We mean it. // -#include <QtCore/qglobal.h> +#include <QtGui/private/qtguiglobal_p.h> -#ifndef QT_NO_SYSTEMTRAYICON +QT_REQUIRE_CONFIG(systemtrayicon); #include <QIcon> #include <QTemporaryFile> @@ -165,5 +165,4 @@ private: QT_END_NAMESPACE -#endif // QT_NO_SYSTEMTRAYICON #endif // QDBUSTRAYICON_H diff --git a/src/platformsupport/themes/genericunix/dbustray/qdbustraytypes_p.h b/src/platformsupport/themes/genericunix/dbustray/qdbustraytypes_p.h index 1bdc855c3c..3f75555579 100644 --- a/src/platformsupport/themes/genericunix/dbustray/qdbustraytypes_p.h +++ b/src/platformsupport/themes/genericunix/dbustray/qdbustraytypes_p.h @@ -52,7 +52,9 @@ // We mean it. // -#ifndef QT_NO_SYSTEMTRAYICON +#include <QtGui/private/qtguiglobal_p.h> + +QT_REQUIRE_CONFIG(systemtrayicon); #include <QObject> #include <QString> @@ -104,5 +106,4 @@ Q_DECLARE_METATYPE(QXdgDBusImageStruct) Q_DECLARE_METATYPE(QXdgDBusImageVector) Q_DECLARE_METATYPE(QXdgDBusToolTipStruct) -#endif // QT_NO_SYSTEMTRAYICON #endif // QDBUSTRAYTYPES_P_H diff --git a/src/platformsupport/themes/genericunix/dbustray/qstatusnotifieritemadaptor_p.h b/src/platformsupport/themes/genericunix/dbustray/qstatusnotifieritemadaptor_p.h index 776e1b23ef..3f8fca7ac0 100644 --- a/src/platformsupport/themes/genericunix/dbustray/qstatusnotifieritemadaptor_p.h +++ b/src/platformsupport/themes/genericunix/dbustray/qstatusnotifieritemadaptor_p.h @@ -62,9 +62,9 @@ // We mean it. // -#include <QtCore/qglobal.h> +#include <QtGui/private/qtguiglobal_p.h> -#ifndef QT_NO_SYSTEMTRAYICON +QT_REQUIRE_CONFIG(systemtrayicon); #include <QtCore/QObject> #include <QtDBus/QtDBus> @@ -200,5 +200,5 @@ private: }; QT_END_NAMESPACE -#endif // QT_NO_SYSTEMTRAYICON + #endif // QSTATUSNOTIFIERITEMADAPTER_P_H diff --git a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp index db264d1b22..1e1b1af4b5 100644 --- a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp +++ b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qgenericunixthemes_p.h" -#include "../../services/genericunix/qgenericunixservices_p.h" #include "qpa/qplatformtheme_p.h" @@ -61,11 +60,11 @@ #include <qpa/qplatformservices.h> #include <qpa/qplatformdialoghelper.h> #ifndef QT_NO_DBUS -#include "QtPlatformSupport/private/qdbusplatformmenu_p.h" -#include "QtPlatformSupport/private/qdbusmenubar_p.h" +#include "qdbusplatformmenu_p.h" +#include "qdbusmenubar_p.h" #endif #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) -#include "QtPlatformSupport/private/qdbustrayicon_p.h" +#include "qdbustrayicon_p.h" #endif #include <algorithm> diff --git a/src/platformsupport/themes/themes.pri b/src/platformsupport/themes/themes.pri deleted file mode 100644 index 552973431f..0000000000 --- a/src/platformsupport/themes/themes.pri +++ /dev/null @@ -1,9 +0,0 @@ -unix:!mac { - include($$PWD/genericunix/genericunix.pri) -} - -HEADERS += \ - $$PWD/qabstractfileiconengine_p.h - -SOURCES += \ - $$PWD/qabstractfileiconengine.cpp diff --git a/src/platformsupport/themes/themes.pro b/src/platformsupport/themes/themes.pro new file mode 100644 index 0000000000..2aeb1f89ad --- /dev/null +++ b/src/platformsupport/themes/themes.pro @@ -0,0 +1,19 @@ +TARGET = QtThemeSupport +MODULE = theme_support + +QT = core-private gui-private +CONFIG += static internal_module + +DEFINES += QT_NO_CAST_FROM_ASCII +PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h + +unix:!darwin: \ + include($$PWD/genericunix/genericunix.pri) + +HEADERS += \ + qabstractfileiconengine_p.h + +SOURCES += \ + qabstractfileiconengine.cpp + +load(qt_module) |