diff options
Diffstat (limited to 'src/platformsupport')
132 files changed, 7498 insertions, 643 deletions
diff --git a/src/platformsupport/accessibility/accessibility.pri b/src/platformsupport/accessibility/accessibility.pri deleted file mode 100644 index fb56edbf1a..0000000000 --- a/src/platformsupport/accessibility/accessibility.pri +++ /dev/null @@ -1,9 +0,0 @@ -contains(QT_CONFIG, 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.mm b/src/platformsupport/cglconvenience/cglconvenience.mm index a4c5c7f61e..85add35879 100644 --- a/src/platformsupport/cglconvenience/cglconvenience.mm +++ b/src/platformsupport/cglconvenience/cglconvenience.mm @@ -81,22 +81,15 @@ void *qcgl_createNSOpenGLPixelFormat(const QSurfaceFormat &format) else if (format.swapBehavior() == QSurfaceFormat::TripleBuffer) attrs.append(NSOpenGLPFATripleBuffer); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { - if (format.profile() == QSurfaceFormat::CoreProfile - && ((format.majorVersion() == 3 && format.minorVersion() >= 2) - || format.majorVersion() > 3)) { - attrs << NSOpenGLPFAOpenGLProfile; - attrs << NSOpenGLProfileVersion3_2Core; - } else { - attrs << NSOpenGLPFAOpenGLProfile; - attrs << NSOpenGLProfileVersionLegacy; - } + if (format.profile() == QSurfaceFormat::CoreProfile + && ((format.majorVersion() == 3 && format.minorVersion() >= 2) + || format.majorVersion() > 3)) { + attrs << NSOpenGLPFAOpenGLProfile; + attrs << NSOpenGLProfileVersion3_2Core; + } else { + attrs << NSOpenGLPFAOpenGLProfile; + attrs << NSOpenGLProfileVersionLegacy; } -#else - if (format.profile() == QSurfaceFormat::CoreProfile) - qWarning("Mac OSX >= 10.7 is needed for OpenGL Core Profile support"); -#endif if (format.depthBufferSize() > 0) attrs << NSOpenGLPFADepthSize << format.depthBufferSize(); 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 70de90c21d..6a6e033bec 100644 --- a/src/platformsupport/clipboard/qmacmime.mm +++ b/src/platformsupport/clipboard/qmacmime.mm @@ -43,7 +43,7 @@ #import <AppKit/AppKit.h> #endif -#if defined(Q_OS_IOS) +#if defined(QT_PLATFORM_UIKIT) #import <UIKit/UIKit.h> #endif @@ -519,11 +519,6 @@ QString QMacPasteboardMimeRtfText::mimeFor(QString flav) bool QMacPasteboardMimeRtfText::canConvert(const QString &mime, QString flav) { -#if defined(Q_OS_IOS) - if (QSysInfo::MacintoshVersion < QSysInfo::MV_IOS_7_0) - return false; -#endif - return mime == mimeFor(flav); } diff --git a/src/platformsupport/devicediscovery/devicediscovery.pri b/src/platformsupport/devicediscovery/devicediscovery.pri deleted file mode 100644 index 9829ae88ba..0000000000 --- a/src/platformsupport/devicediscovery/devicediscovery.pri +++ /dev/null @@ -1,14 +0,0 @@ -HEADERS += $$PWD/qdevicediscovery_p.h - -contains(QT_CONFIG, libudev) { - SOURCES += $$PWD/qdevicediscovery_udev.cpp - HEADERS += $$PWD/qdevicediscovery_udev_p.h - INCLUDEPATH += $$QMAKE_INCDIR_LIBUDEV - LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV -} else: contains(QT_CONFIG, 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/devicediscovery/qdevicediscovery_static.cpp b/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp index 135ffa2494..5c72dbe7e2 100644 --- a/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp +++ b/src/platformsupport/devicediscovery/qdevicediscovery_static.cpp @@ -101,7 +101,8 @@ QStringList QDeviceDiscoveryStatic::scanConnectedDevices() // check for input devices if (m_types & Device_InputMask) { dir.setPath(QString::fromLatin1(QT_EVDEV_DEVICE_PATH)); - foreach (const QString &deviceFile, dir.entryList()) { + const auto deviceFiles = dir.entryList(); + for (const QString &deviceFile : deviceFiles) { QString absoluteFilePath = dir.absolutePath() + QLatin1Char('/') + deviceFile; if (checkDeviceType(absoluteFilePath)) devices << absoluteFilePath; @@ -111,7 +112,8 @@ QStringList QDeviceDiscoveryStatic::scanConnectedDevices() // check for drm devices if (m_types & Device_VideoMask) { dir.setPath(QString::fromLatin1(QT_DRM_DEVICE_PATH)); - foreach (const QString &deviceFile, dir.entryList()) { + const auto deviceFiles = dir.entryList(); + for (const QString &deviceFile : deviceFiles) { QString absoluteFilePath = dir.absolutePath() + QLatin1Char('/') + deviceFile; if (checkDeviceType(absoluteFilePath)) devices << absoluteFilePath; @@ -133,7 +135,7 @@ bool QDeviceDiscoveryStatic::checkDeviceType(const QString &device) qCDebug(lcDD) << "doing static device discovery for " << device; - if ((m_types & Device_DRM) && device.contains(QString::fromLatin1(QT_DRM_DEVICE_PREFIX))) { + if ((m_types & Device_DRM) && device.contains(QLatin1String(QT_DRM_DEVICE_PREFIX))) { QT_CLOSE(fd); return true; } diff --git a/src/platformsupport/eglconvenience/eglconvenience.pri b/src/platformsupport/eglconvenience/eglconvenience.pri deleted file mode 100644 index 1cacaf4a4c..0000000000 --- a/src/platformsupport/eglconvenience/eglconvenience.pri +++ /dev/null @@ -1,32 +0,0 @@ -contains(QT_CONFIG,egl) { - HEADERS += \ - $$PWD/qeglconvenience_p.h \ - $$PWD/qeglstreamconvenience_p.h \ - $$PWD/qt_egl_p.h - - SOURCES += \ - $$PWD/qeglconvenience.cpp \ - $$PWD/qeglstreamconvenience.cpp - - contains(QT_CONFIG,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 - - contains(QT_CONFIG,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 f535afbc55..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. @@ -140,6 +140,10 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMEROUTPUTEXTPROC) (EGLDisplay #define EGL_DRM_PLANE_EXT 0x3235 #endif +#ifndef EGL_PLATFORM_X11_KHR +#define EGL_PLATFORM_X11_KHR 0x31D5 +#endif + QT_BEGIN_NAMESPACE class QEGLStreamConvenience diff --git a/src/platformsupport/eventdispatchers/eventdispatchers.pri b/src/platformsupport/eventdispatchers/eventdispatchers.pri deleted file mode 100644 index a0b37cae1a..0000000000 --- a/src/platformsupport/eventdispatchers/eventdispatchers.pri +++ /dev/null @@ -1,22 +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 -} - -contains(QT_CONFIG, glib) { - SOURCES +=$$PWD/qeventdispatcher_glib.cpp - HEADERS +=$$PWD/qeventdispatcher_glib_p.h - QMAKE_CXXFLAGS += $$QT_CFLAGS_GLIB - LIBS_PRIVATE += $$QT_LIBS_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/eventdispatchers/qeventdispatcher_glib_p.h b/src/platformsupport/eventdispatchers/qeventdispatcher_glib_p.h index 7d57b97276..6d148753bf 100644 --- a/src/platformsupport/eventdispatchers/qeventdispatcher_glib_p.h +++ b/src/platformsupport/eventdispatchers/qeventdispatcher_glib_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // diff --git a/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher.cpp b/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher.cpp index 7d49c35d84..de369e0b00 100644 --- a/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher.cpp +++ b/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher.cpp @@ -151,11 +151,10 @@ messageDebugEntries[] = { {WM_IME_ENDCOMPOSITION, "WM_IME_ENDCOMPOSITION", true}, {WM_IME_NOTIFY, "WM_IME_NOTIFY", true}, {WM_IME_REQUEST, "WM_IME_REQUEST", true}, -#if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) +#if !defined(QT_NO_SESSIONMANAGER) {WM_QUERYENDSESSION, "WM_QUERYENDSESSION", true}, {WM_ENDSESSION, "WM_ENDSESSION", true}, #endif -#ifndef Q_OS_WINCE {WM_MOUSEACTIVATE,"WM_MOUSEACTIVATE", true}, {WM_CHILDACTIVATE, "WM_CHILDACTIVATE", true}, {WM_PARENTNOTIFY, "WM_PARENTNOTIFY", true}, @@ -181,7 +180,6 @@ messageDebugEntries[] = { {WM_CHANGECBCHAIN, "WM_CHANGECBCHAIN", true}, {WM_DISPLAYCHANGE, "WM_DISPLAYCHANGE", true}, {WM_DRAWCLIPBOARD, "WM_DRAWCLIPBOARD", true}, -#endif // !Q_OS_WINCE {WM_THEMECHANGED, "WM_THEMECHANGED", true} }; diff --git a/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher_p.h b/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher_p.h index 5682fc72ef..9a23b5c0bd 100644 --- a/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher_p.h +++ b/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher_p.h @@ -44,9 +44,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// 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. // 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/fbconvenience/qfbbackingstore.cpp b/src/platformsupport/fbconvenience/qfbbackingstore.cpp index fa1b8d0acd..2cad3441e4 100644 --- a/src/platformsupport/fbconvenience/qfbbackingstore.cpp +++ b/src/platformsupport/fbconvenience/qfbbackingstore.cpp @@ -81,6 +81,12 @@ const QImage QFbBackingStore::image() return mImage; } + +QImage QFbBackingStore::toImage() const +{ + return mImage; +} + void QFbBackingStore::lock() { mImageMutex.lock(); @@ -110,4 +116,3 @@ void QFbBackingStore::endPaint() } QT_END_NAMESPACE - diff --git a/src/platformsupport/fbconvenience/qfbbackingstore_p.h b/src/platformsupport/fbconvenience/qfbbackingstore_p.h index fc06b95475..c8dfe3489c 100644 --- a/src/platformsupport/fbconvenience/qfbbackingstore_p.h +++ b/src/platformsupport/fbconvenience/qfbbackingstore_p.h @@ -72,6 +72,7 @@ public: void resize(const QSize &size, const QRegion ®ion) Q_DECL_OVERRIDE; const QImage image(); + QImage toImage() const override; void lock(); void unlock(); diff --git a/src/platformsupport/fbconvenience/qfbscreen.cpp b/src/platformsupport/fbconvenience/qfbscreen.cpp index 4066743cc2..216f2722a4 100644 --- a/src/platformsupport/fbconvenience/qfbscreen.cpp +++ b/src/platformsupport/fbconvenience/qfbscreen.cpp @@ -138,15 +138,16 @@ void QFbScreen::lower(QFbWindow *window) QWindow *QFbScreen::topWindow() const { - foreach (QFbWindow *fbw, mWindowStack) + for (QFbWindow *fbw : mWindowStack) { if (fbw->window()->type() == Qt::Window || fbw->window()->type() == Qt::Dialog) return fbw->window(); + } return 0; } QWindow *QFbScreen::topLevelAt(const QPoint & p) const { - foreach (QFbWindow *fbw, mWindowStack) { + for (QFbWindow *fbw : mWindowStack) { if (fbw->geometry().contains(p, false) && fbw->window()->isVisible()) return fbw->window(); } @@ -206,15 +207,13 @@ void QFbScreen::generateRects() remainingScreen -= localGeometry; QRegion windowRegion(localGeometry); windowRegion -= remainingScreen; - foreach (const QRect &rect, windowRegion.rects()) { + for (const QRect &rect : windowRegion) mCachedRects += QPair<QRect, int>(rect, i); - } } #endif } - const QVector<QRect> remainingScreenRects = remainingScreen.rects(); - mCachedRects.reserve(mCachedRects.count() + remainingScreenRects.count()); - foreach (const QRect &rect, remainingScreenRects) + mCachedRects.reserve(mCachedRects.count() + remainingScreen.rectCount()); + for (const QRect &rect : remainingScreen) mCachedRects += QPair<QRect, int>(rect, -1); mIsUpToDate = true; } @@ -254,7 +253,7 @@ QRegion QFbScreen::doRedraw() rectRegion -= intersect; // we only expect one rectangle, but defensive coding... - foreach (const QRect &rect, intersect.rects()) { + for (const QRect &rect : intersect) { bool firstLayer = true; if (layer == -1) { mCompositePainter->setCompositionMode(QPainter::CompositionMode_Source); diff --git a/src/platformsupport/fbconvenience/qfbvthandler.cpp b/src/platformsupport/fbconvenience/qfbvthandler.cpp index 4c4a01a82e..102bc91647 100644 --- a/src/platformsupport/fbconvenience/qfbvthandler.cpp +++ b/src/platformsupport/fbconvenience/qfbvthandler.cpp @@ -39,8 +39,9 @@ #include "qfbvthandler_p.h" #include <QtCore/QSocketNotifier> +#include <QtCore/private/qglobal_p.h> -#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && (!defined(QT_NO_EVDEV) || !defined(QT_NO_LIBINPUT)) +#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && (QT_CONFIG(evdev) || QT_CONFIG(libinput)) #define VTH_ENABLED @@ -171,7 +172,7 @@ void QFbVtHandler::handleSignal() char sigNo; if (QT_READ(m_sigFd[1], &sigNo, sizeof(sigNo)) == sizeof(sigNo)) { switch (sigNo) { - case SIGINT: // fallthrough + case SIGINT: case SIGTERM: handleInt(); break; diff --git a/src/platformsupport/fbconvenience/qfbvthandler_p.h b/src/platformsupport/fbconvenience/qfbvthandler_p.h index e05a9d3ffc..17d07317b2 100644 --- a/src/platformsupport/fbconvenience/qfbvthandler_p.h +++ b/src/platformsupport/fbconvenience/qfbvthandler_p.h @@ -51,6 +51,7 @@ // We mean it. // +#include <QtGui/private/qtguiglobal_p.h> #include <QObject> QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/fontdatabases/basic/basic.pri b/src/platformsupport/fontdatabases/basic/basic.pri index d70b3b6a1a..c50dba3ce2 100644 --- a/src/platformsupport/fontdatabases/basic/basic.pri +++ b/src/platformsupport/fontdatabases/basic/basic.pri @@ -1,5 +1,3 @@ -DEFINES += QT_NO_FONTCONFIG - HEADERS += \ $$PWD/qbasicfontdatabase_p.h \ $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft_p.h @@ -8,6 +6,4 @@ SOURCES += \ $$PWD/qbasicfontdatabase.cpp \ $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft.cpp -CONFIG += opentype - -include($$QT_SOURCE_TREE/src/3rdparty/freetype_dependency.pri) +QMAKE_USE += freetype diff --git a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp index 4b9d4690f3..60ddc9fa23 100644 --- a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp @@ -77,7 +77,8 @@ void QBasicFontDatabase::populateFontDatabase() << QLatin1String("*.pfb") << QLatin1String("*.otf"); - foreach (const QFileInfo &fi, dir.entryInfoList(nameFilters, QDir::Files)) { + const auto fis = dir.entryInfoList(nameFilters, QDir::Files); + for (const QFileInfo &fi : fis) { const QByteArray file = QFile::encodeName(fi.absoluteFilePath()); QBasicFontDatabase::addTTFile(QByteArray(), file); } diff --git a/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri b/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri index bd7294b818..6458464870 100644 --- a/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri +++ b/src/platformsupport/fontdatabases/fontconfig/fontconfig.pri @@ -2,5 +2,5 @@ HEADERS += $$PWD/qfontconfigdatabase_p.h \ $$PWD/qfontenginemultifontconfig_p.h SOURCES += $$PWD/qfontconfigdatabase.cpp \ $$PWD/qfontenginemultifontconfig.cpp -DEFINES -= QT_NO_FONTCONFIG -QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_FONTCONFIG + +QMAKE_USE += fontconfig diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index d5cb02e291..65bc494e91 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -510,7 +510,7 @@ static void populateFromPattern(FcPattern *pattern) void QFontconfigDatabase::populateFontDatabase() { - FcInitReinitialize(); + FcInit(); FcFontSet *fonts; { @@ -574,6 +574,12 @@ void QFontconfigDatabase::populateFontDatabase() // QApplication::setFont(font); } +void QFontconfigDatabase::invalidate() +{ + // Clear app fonts. + FcConfigAppFontClear(0); +} + QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) { return new QFontEngineMultiFontConfig(fontEngine, script); diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase_p.h index 197a442d5b..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 @@ -62,6 +62,7 @@ class QFontconfigDatabase : public QBasicFontDatabase { public: void populateFontDatabase() Q_DECL_OVERRIDE; + void invalidate() 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; diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontenginemultifontconfig.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontenginemultifontconfig.cpp index ad5908dcc7..7574f9f9e6 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontenginemultifontconfig.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontenginemultifontconfig.cpp @@ -50,7 +50,7 @@ QFontEngineMultiFontConfig::QFontEngineMultiFontConfig(QFontEngine *fe, int scri QFontEngineMultiFontConfig::~QFontEngineMultiFontConfig() { - Q_FOREACH (FcPattern *pattern, cachedMatchPatterns) { + for (FcPattern *pattern : qAsConst(cachedMatchPatterns)) { if (pattern) FcPatternDestroy(pattern); } diff --git a/src/platformsupport/fontdatabases/fontdatabases.pri b/src/platformsupport/fontdatabases/fontdatabases.pri deleted file mode 100644 index 12b06d64cc..0000000000 --- a/src/platformsupport/fontdatabases/fontdatabases.pri +++ /dev/null @@ -1,16 +0,0 @@ -!win32|contains(QT_CONFIG, freetype):!mac { - include($$PWD/basic/basic.pri) -} - -unix:!mac { - CONFIG += qpa/genericunixfontdatabase - include($$PWD/genericunix/genericunix.pri) - contains(QT_CONFIG,fontconfig) { - include($$PWD/fontconfig/fontconfig.pri) - } -} - -mac { - include($$PWD/mac/coretext.pri) -} - diff --git a/src/platformsupport/fontdatabases/fontdatabases.pro b/src/platformsupport/fontdatabases/fontdatabases.pro new file mode 100644 index 0000000000..9376c3b702 --- /dev/null +++ b/src/platformsupport/fontdatabases/fontdatabases.pro @@ -0,0 +1,33 @@ +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:!if(watchos:CONFIG(simulator, simulator|device)) { + 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) + } + + winrt { + include($$PWD/winrt/winrt.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 ebb64d15b4..1caeb2c1ac 100644 --- a/src/platformsupport/fontdatabases/mac/coretext.pri +++ b/src/platformsupport/fontdatabases/mac/coretext.pri @@ -1,15 +1,14 @@ HEADERS += $$PWD/qcoretextfontdatabase_p.h $$PWD/qfontengine_coretext_p.h OBJECTIVE_SOURCES += $$PWD/qfontengine_coretext.mm $$PWD/qcoretextfontdatabase.mm -contains(QT_CONFIG, freetype) { - include($$QT_SOURCE_TREE/src/3rdparty/freetype_dependency.pri) +qtConfig(freetype) { + QMAKE_USE += freetype HEADERS += $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft_p.h SOURCES += $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft.cpp - CONFIG += opentype } -ios: \ - # On iOS CoreText and CoreGraphics are stand-alone frameworks +uikit: \ + # On iOS/tvOS/watchOS CoreText and CoreGraphics are stand-alone frameworks LIBS_PRIVATE += -framework CoreText -framework CoreGraphics else: \ # On Mac OS they are part of the ApplicationServices umbrella framework, diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index 5be7e3aadc..78a0c4d0c9 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -44,7 +44,7 @@ #if defined(Q_OS_OSX) #import <AppKit/AppKit.h> #import <IOKit/graphics/IOGraphicsLib.h> -#elif defined(Q_OS_IOS) +#elif defined(QT_PLATFORM_UIKIT) #import <UIKit/UIFont.h> #endif @@ -183,7 +183,7 @@ QCoreTextFontDatabase::QCoreTextFontDatabase(bool useFreeType) QCoreTextFontDatabase::~QCoreTextFontDatabase() { - foreach (CTFontDescriptorRef ref, m_systemFontDescriptors) + for (CTFontDescriptorRef ref : qAsConst(m_systemFontDescriptors)) CFRelease(ref); } @@ -191,7 +191,7 @@ static CFArrayRef availableFamilyNames() { #if defined(Q_OS_OSX) return CTFontManagerCopyAvailableFontFamilyNames(); -#elif defined(Q_OS_IOS) +#elif defined(QT_PLATFORM_UIKIT) return (CFArrayRef) [[UIFont familyNames] retain]; #endif } @@ -206,7 +206,7 @@ void QCoreTextFontDatabase::populateFontDatabase() const int numberOfFamilies = CFArrayGetCount(familyNames); for (int i = 0; i < numberOfFamilies; ++i) { CFStringRef familyNameRef = (CFStringRef) CFArrayGetValueAtIndex(familyNames, i); - QString familyName = QCFString::toQString(familyNameRef); + QString familyName = QString::fromCFString(familyNameRef); // Don't populate internal fonts if (familyName.startsWith(QLatin1Char('.')) || familyName == QLatin1String("LastResort")) @@ -505,9 +505,9 @@ static QString familyNameFromPostScriptName(NSString *psName) { QCFType<CTFontDescriptorRef> fontDescriptor = (CTFontDescriptorRef) CTFontDescriptorCreateWithNameAndSize((CFStringRef)psName, 12.0); QCFString familyName = (CFStringRef) CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute); - QString name = QCFString::toQString(familyName); + QString name = QString::fromCFString(familyName); if (name.isEmpty()) - qWarning() << "QCoreTextFontDatabase: Failed to resolve family name for PostScript name " << QCFString::toQString((CFStringRef)psName); + qWarning() << "QCoreTextFontDatabase: Failed to resolve family name for PostScript name " << QString::fromCFString((CFStringRef)psName); return name; } @@ -523,46 +523,37 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo static QHash<QString, QStringList> fallbackLists; if (!family.isEmpty()) { -#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_8, __IPHONE_6_0) - // CTFontCopyDefaultCascadeListForLanguages is available in the SDK - #if QT_MAC_DEPLOYMENT_TARGET_BELOW(__MAC_10_8, __IPHONE_6_0) - // But we have to feature check at runtime - if (&CTFontCopyDefaultCascadeListForLanguages) - #endif - { - QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(family)); - if (QCFType<CTFontDescriptorRef> fontDescriptor = CTFontDescriptorCreateWithAttributes(attributes)) { - if (QCFType<CTFontRef> font = CTFontCreateWithFontDescriptor(fontDescriptor, 12.0, 0)) { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"]; - - QCFType<CFArrayRef> cascadeList = (CFArrayRef) CTFontCopyDefaultCascadeListForLanguages(font, (CFArrayRef) languages); - if (cascadeList) { - QStringList fallbackList; - const int numCascades = CFArrayGetCount(cascadeList); - for (int i = 0; i < numCascades; ++i) { - CTFontDescriptorRef fontFallback = (CTFontDescriptorRef) CFArrayGetValueAtIndex(cascadeList, i); - QCFString fallbackFamilyName = (CFStringRef) CTFontDescriptorCopyAttribute(fontFallback, kCTFontFamilyNameAttribute); - fallbackList.append(QCFString::toQString(fallbackFamilyName)); - } + QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(family)); + if (QCFType<CTFontDescriptorRef> fontDescriptor = CTFontDescriptorCreateWithAttributes(attributes)) { + if (QCFType<CTFontRef> font = CTFontCreateWithFontDescriptor(fontDescriptor, 12.0, 0)) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"]; + + QCFType<CFArrayRef> cascadeList = (CFArrayRef) CTFontCopyDefaultCascadeListForLanguages(font, (CFArrayRef) languages); + if (cascadeList) { + QStringList fallbackList; + const int numCascades = CFArrayGetCount(cascadeList); + for (int i = 0; i < numCascades; ++i) { + CTFontDescriptorRef fontFallback = (CTFontDescriptorRef) CFArrayGetValueAtIndex(cascadeList, i); + QCFString fallbackFamilyName = (CFStringRef) CTFontDescriptorCopyAttribute(fontFallback, kCTFontFamilyNameAttribute); + fallbackList.append(QString::fromCFString(fallbackFamilyName)); + } #if defined(Q_OS_OSX) - // Since we are only returning a list of default fonts for the current language, we do not - // cover all unicode completely. This was especially an issue for some of the common script - // symbols such as mathematical symbols, currency or geometric shapes. To minimize the risk - // of missing glyphs, we add Arial Unicode MS as a final fail safe, since this covers most - // of Unicode 2.1. - if (!fallbackList.contains(QStringLiteral("Arial Unicode MS"))) - fallbackList.append(QStringLiteral("Arial Unicode MS")); + // Since we are only returning a list of default fonts for the current language, we do not + // cover all unicode completely. This was especially an issue for some of the common script + // symbols such as mathematical symbols, currency or geometric shapes. To minimize the risk + // of missing glyphs, we add Arial Unicode MS as a final fail safe, since this covers most + // of Unicode 2.1. + if (!fallbackList.contains(QStringLiteral("Arial Unicode MS"))) + fallbackList.append(QStringLiteral("Arial Unicode MS")); #endif - return fallbackList; - } + return fallbackList; } } } -#endif } // We were not able to find a fallback for the specific family, @@ -625,7 +616,6 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo return fallbackLists[styleLookupKey.arg(styleHint)]; } -#if HAVE_CORETEXT static CFArrayRef createDescriptorArrayForFont(CTFontRef font, const QString &fileName = QString()) { CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); @@ -643,7 +633,7 @@ static CFArrayRef createDescriptorArrayForFont(CTFontRef font, const QString &fi // QUrl::fromLocalFile() doesn't accept qrc pseudo-paths like ":/fonts/myfont.ttf". // Therefore construct from QString with the qrc:// scheme -> "qrc:///fonts/myfont.ttf". fontURL = QUrl(QStringLiteral("qrc://") + fileName.mid(1)).toCFURL(); - } else if (!fileName.isEmpty()) { + } else { // At this point we hope that filename is in a format that QUrl can handle. fontURL = QUrl::fromLocalFile(fileName).toCFURL(); } @@ -658,104 +648,41 @@ static CFArrayRef createDescriptorArrayForFont(CTFontRef font, const QString &fi CFArrayAppendValue(array, descriptor); return array; } -#endif QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) { QCFType<CFArrayRef> fonts; QStringList families; -#if HAVE_CORETEXT - if (&CTFontManagerRegisterGraphicsFont) { - CFErrorRef error = 0; - if (!fontData.isEmpty()) { - QByteArray* fontDataCopy = new QByteArray(fontData); - QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(fontDataCopy, - fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); - QCFType<CGFontRef> cgFont = CGFontCreateWithDataProvider(dataProvider); - if (cgFont) { - if (CTFontManagerRegisterGraphicsFont(cgFont, &error)) { - QCFType<CTFontRef> font = CTFontCreateWithGraphicsFont(cgFont, 0.0, NULL, NULL); - fonts = createDescriptorArrayForFont(font + CFErrorRef error = 0; + if (!fontData.isEmpty()) { + QByteArray* fontDataCopy = new QByteArray(fontData); + QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(fontDataCopy, + fontDataCopy->constData(), fontDataCopy->size(), releaseFontData); + QCFType<CGFontRef> cgFont = CGFontCreateWithDataProvider(dataProvider); + if (cgFont) { + if (CTFontManagerRegisterGraphicsFont(cgFont, &error)) { + QCFType<CTFontRef> font = CTFontCreateWithGraphicsFont(cgFont, 0.0, NULL, NULL); + fonts = createDescriptorArrayForFont(font #ifndef QT_NO_FREETYPE - , m_useFreeType ? fileName : QString() -#endif - ); - m_applicationFonts.append(QVariant::fromValue(QCFType<CGFontRef>::constructFromGet(cgFont))); - } - } - } else { - QCFType<CFURLRef> fontURL = CFURLCreateWithFileSystemPath(NULL, QCFString(fileName), kCFURLPOSIXPathStyle, false); - if (CTFontManagerRegisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &error)) { -#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_6, __IPHONE_7_0) - if (&CTFontManagerCreateFontDescriptorsFromURL) - fonts = CTFontManagerCreateFontDescriptorsFromURL(fontURL); - else + , m_useFreeType ? fileName : QString() #endif - { - // We're limited to a single font per file, unless we dive into the font tables - QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attributes, kCTFontURLAttribute, fontURL); - QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithAttributes(attributes); - QCFType<CTFontRef> font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL); - fonts = createDescriptorArrayForFont(font); - } - - m_applicationFonts.append(QVariant::fromValue(QCFType<CFURLRef>::constructFromGet(fontURL))); + ); + m_applicationFonts.append(QVariant::fromValue(QCFType<CGFontRef>::constructFromGet(cgFont))); } } - - if (error) { - NSLog(@"Unable to register font: %@", error); - CFRelease(error); + } else { + QCFType<CFURLRef> fontURL = CFURLCreateWithFileSystemPath(NULL, QCFString(fileName), kCFURLPOSIXPathStyle, false); + if (CTFontManagerRegisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &error)) { + fonts = CTFontManagerCreateFontDescriptorsFromURL(fontURL); + m_applicationFonts.append(QVariant::fromValue(QCFType<CFURLRef>::constructFromGet(fontURL))); } } -#endif -#if HAVE_CORETEXT && HAVE_ATS - else -#endif -#if HAVE_ATS - { - ATSFontContainerRef fontContainer; - OSStatus e; - - if (!fontData.isEmpty()) { - e = ATSFontActivateFromMemory((void *) fontData.constData(), fontData.size(), - kATSFontContextLocal, kATSFontFormatUnspecified, NULL, - kATSOptionFlagsDefault, &fontContainer); - } else { - FSRef ref; - if (FSPathMakeRef(reinterpret_cast<const UInt8 *>(fileName.toUtf8().constData()), - &ref, 0) != noErr) - return QStringList(); - e = ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0, - kATSOptionFlagsDefault, &fontContainer); - } - - if (e == noErr) { - ItemCount fontCount = 0; - e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, 0, 0, &fontCount); - if (e != noErr) - return QStringList(); - - QVarLengthArray<ATSFontRef> containedFonts(fontCount); - e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount); - if (e != noErr) - return QStringList(); - - CFMutableArrayRef fontsArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - for (int i = 0; i < containedFonts.size(); ++i) { - QCFType<CTFontRef> font = CTFontCreateWithPlatformFont(containedFonts[i], 12.0, NULL, NULL); - CFArrayAppendValue(fontsArray, QCFType<CTFontDescriptorRef>(CTFontCopyFontDescriptor(font))); - } - fonts = fontsArray; - - m_applicationFonts.append(QVariant::fromValue(fontContainer)); - } + if (error) { + NSLog(@"Unable to register font: %@", error); + CFRelease(error); } -#endif if (fonts) { const int numFonts = CFArrayGetCount(fonts); @@ -852,41 +779,39 @@ static CTFontUIFontType fontTypeFromTheme(QPlatformTheme::Font f) static CTFontDescriptorRef fontDescriptorFromTheme(QPlatformTheme::Font f) { -#ifdef Q_OS_IOS - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0) { - // Use Dynamic Type to resolve theme fonts if possible, to get - // correct font sizes and style based on user configuration. - NSString *textStyle = 0; - switch (f) { - case QPlatformTheme::TitleBarFont: - case QPlatformTheme::HeaderViewFont: - textStyle = UIFontTextStyleHeadline; - break; - case QPlatformTheme::MdiSubWindowTitleFont: - textStyle = UIFontTextStyleSubheadline; - break; - case QPlatformTheme::TipLabelFont: - case QPlatformTheme::SmallFont: - textStyle = UIFontTextStyleFootnote; - break; - case QPlatformTheme::MiniFont: - textStyle = UIFontTextStyleCaption2; - break; - case QPlatformTheme::FixedFont: - // Fall back to regular code path, as iOS doesn't provide - // an appropriate text style for this theme font. - break; - default: - textStyle = UIFontTextStyleBody; - break; - } +#if defined(QT_PLATFORM_UIKIT) + // Use Dynamic Type to resolve theme fonts if possible, to get + // correct font sizes and style based on user configuration. + NSString *textStyle = 0; + switch (f) { + case QPlatformTheme::TitleBarFont: + case QPlatformTheme::HeaderViewFont: + textStyle = UIFontTextStyleHeadline; + break; + case QPlatformTheme::MdiSubWindowTitleFont: + textStyle = UIFontTextStyleSubheadline; + break; + case QPlatformTheme::TipLabelFont: + case QPlatformTheme::SmallFont: + textStyle = UIFontTextStyleFootnote; + break; + case QPlatformTheme::MiniFont: + textStyle = UIFontTextStyleCaption2; + break; + case QPlatformTheme::FixedFont: + // Fall back to regular code path, as iOS doesn't provide + // an appropriate text style for this theme font. + break; + default: + textStyle = UIFontTextStyleBody; + break; + } - if (textStyle) { - UIFontDescriptor *desc = [UIFontDescriptor preferredFontDescriptorWithTextStyle:textStyle]; - return static_cast<CTFontDescriptorRef>(CFBridgingRetain(desc)); - } + if (textStyle) { + UIFontDescriptor *desc = [UIFontDescriptor preferredFontDescriptorWithTextStyle:textStyle]; + return static_cast<CTFontDescriptorRef>(CFBridgingRetain(desc)); } -#endif // Q_OS_IOS +#endif // Q_OS_IOS, Q_OS_TVOS, Q_OS_WATCHOS // OSX default case and iOS fallback case CTFontUIFontType fontType = fontTypeFromTheme(f); @@ -952,32 +877,16 @@ void QCoreTextFontDatabase::removeApplicationFonts() if (m_applicationFonts.isEmpty()) return; - foreach (const QVariant &font, m_applicationFonts) { -#if HAVE_CORETEXT - if (&CTFontManagerUnregisterGraphicsFont && &CTFontManagerUnregisterFontsForURL) { - CFErrorRef error; - if (font.canConvert(qMetaTypeId<QCFType<CGFontRef> >())) { - CTFontManagerUnregisterGraphicsFont(font.value<QCFType<CGFontRef> >(), &error); - } else if (font.canConvert(qMetaTypeId<QCFType<CFURLRef> >())) { - CTFontManagerUnregisterFontsForURL(font.value<QCFType<CFURLRef> >(), kCTFontManagerScopeProcess, &error); - } + for (const QVariant &font : qAsConst(m_applicationFonts)) { + CFErrorRef error; + if (font.canConvert(qMetaTypeId<QCFType<CGFontRef> >())) { + CTFontManagerUnregisterGraphicsFont(font.value<QCFType<CGFontRef> >(), &error); + } else if (font.canConvert(qMetaTypeId<QCFType<CFURLRef> >())) { + CTFontManagerUnregisterFontsForURL(font.value<QCFType<CFURLRef> >(), kCTFontManagerScopeProcess, &error); } -#endif -#if HAVE_CORETEXT && HAVE_ATS - else -#endif -#if HAVE_ATS - if (font.canConvert(qMetaTypeId<ATSFontContainerRef>())) { - ATSFontDeactivate(font.value<ATSFontContainerRef>(), 0, kATSOptionFlagsDoNotNotify); - } -#endif } m_applicationFonts.clear(); - -#if HAVE_ATS - ATSFontNotify(kATSFontNotifyActionFontsChanged, 0); -#endif } #ifndef QT_NO_FREETYPE diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h index 2cc6b09a15..1bc3522bda 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h @@ -52,27 +52,20 @@ // #include <qglobal.h> -#define HAVE_CORETEXT QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_8, __IPHONE_4_1) -#define HAVE_ATS QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_5, __IPHONE_NA) #include <qpa/qplatformfontdatabase.h> #include <qpa/qplatformtheme.h> #include <private/qcore_mac_p.h> -#ifndef Q_OS_IOS +#ifdef Q_OS_OSX #include <ApplicationServices/ApplicationServices.h> #else #include <CoreText/CoreText.h> #include <CoreGraphics/CoreGraphics.h> #endif -#if HAVE_CORETEXT Q_DECLARE_METATYPE(QCFType<CGFontRef>); Q_DECLARE_METATYPE(QCFType<CFURLRef>); -#endif -#if HAVE_ATS -Q_DECLARE_METATYPE(ATSFontContainerRef); -#endif QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm index d11676ced7..7a06d5f1c9 100644 --- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm @@ -51,7 +51,7 @@ #import <AppKit/AppKit.h> #endif -#if defined(Q_OS_IOS) && !QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_8_2) +#if defined(QT_PLATFORM_UIKIT) && !QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_8_2) #import <UIKit/UIKit.h> #endif @@ -78,7 +78,7 @@ #define kCTFontWeightBold NSFontWeightBold #define kCTFontWeightHeavy NSFontWeightHeavy #define kCTFontWeightBlack NSFontWeightBlack -#elif defined(Q_OS_IOS) +#elif defined(QT_PLATFORM_UIKIT) #define kCTFontWeightUltraLight UIFontWeightUltraLight #define kCTFontWeightThin UIFontWeightThin #define kCTFontWeightLight UIFontWeightLight @@ -172,7 +172,7 @@ QFontEngine::GlyphFormat QCoreTextFontEngine::defaultGlyphFormat = QFontEngine:: CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef) { CGAffineTransform transform = CGAffineTransformIdentity; - if (fontDef.stretch != 100) + if (fontDef.stretch && fontDef.stretch != 100) transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); return transform; } @@ -213,7 +213,7 @@ void QCoreTextFontEngine::init() face_id.index = 0; QCFString name = CTFontCopyName(ctfont, kCTFontUniqueNameKey); - face_id.filename = QCFString::toQString(name).toUtf8(); + face_id.filename = QString::fromCFString(name).toUtf8(); QCFString family = CTFontCopyFamilyName(ctfont); fontDef.family = family; @@ -256,6 +256,9 @@ void QCoreTextFontEngine::init() } else avgCharWidth = QFontEngine::averageCharWidth(); + underlineThickness = QFixed::fromReal(CTFontGetUnderlineThickness(ctfont)); + underlinePos = -QFixed::fromReal(CTFontGetUnderlinePosition(ctfont)); + cache_cost = (CTFontGetAscent(ctfont) + CTFontGetDescent(ctfont)) * avgCharWidth.toInt() * 2000; // HACK hb_coretext requires both CTFont and CGFont but user_data is only void* @@ -375,6 +378,19 @@ QFixed QCoreTextFontEngine::ascent() const ? QFixed::fromReal(CTFontGetAscent(ctfont)).round() : QFixed::fromReal(CTFontGetAscent(ctfont)); } + +QFixed QCoreTextFontEngine::capHeight() const +{ + QFixed c = QFixed::fromReal(CTFontGetCapHeight(ctfont)); + if (c <= 0) + return calculatedCapHeight(); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + c = c.round(); + + return c; +} + QFixed QCoreTextFontEngine::descent() const { QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont)); @@ -437,31 +453,25 @@ void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextIt CGContextSetTextDrawingMode(ctx, kCGTextFill); - - QVarLengthArray<CGSize> advances(glyphs.size()); + QVarLengthArray<CGPoint> cgPositions(glyphs.size()); QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size()); - - for (int i = 0; i < glyphs.size() - 1; ++i) { - advances[i].width = (positions[i + 1].x - positions[i].x).toReal(); - advances[i].height = (positions[i + 1].y - positions[i].y).toReal(); + const qreal firstX = positions[0].x.toReal(); + const qreal firstY = positions[0].y.toReal(); + for (int i = 0; i < glyphs.size(); ++i) { + cgPositions[i].x = positions[i].x.toReal() - firstX; + cgPositions[i].y = positions[i].y.toReal() - firstY; cgGlyphs[i] = glyphs[i]; } - advances[glyphs.size() - 1].width = 0; - advances[glyphs.size() - 1].height = 0; - cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1]; - CGContextSetFont(ctx, cgFont); //NSLog(@"Font inDraw %@ ctfont %@", CGFontCopyFullName(cgFont), CTFontCopyFamilyName(ctfont)); CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal()); - - CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); + CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx); if (synthesisFlags & QFontEngine::SynthesizedBold) { CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(), positions[0].y.toReal()); - - CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); + CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx); } CGContextSetTextMatrix(ctx, oldTextMatrix); @@ -606,7 +616,7 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition if (!im.width() || !im.height()) return im; -#ifndef Q_OS_IOS +#ifdef Q_OS_OSX CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); #else CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); @@ -645,14 +655,13 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition CGContextSetTextMatrix(ctx, cgMatrix); CGContextSetRGBFillColor(ctx, 1, 1, 1, 1); CGContextSetTextDrawingMode(ctx, kCGTextFill); - CGContextSetFont(ctx, cgFont); CGContextSetTextPosition(ctx, pos_x, pos_y); - CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &CGSizeZero, 1); + CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx); if (synthesisFlags & QFontEngine::SynthesizedBold) { CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y); - CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &CGSizeZero, 1); + CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx); } } else { // CGContextSetTextMatrix does not work with color glyphs, so we use @@ -800,6 +809,16 @@ bool QCoreTextFontEngine::supportsTransformation(const QTransform &transform) co return false; } +QFixed QCoreTextFontEngine::lineThickness() const +{ + return underlineThickness; +} + +QFixed QCoreTextFontEngine::underlinePosition() const +{ + return underlinePos; +} + QFontEngine::Properties QCoreTextFontEngine::properties() const { Properties result; @@ -807,8 +826,8 @@ QFontEngine::Properties QCoreTextFontEngine::properties() const QCFString psName, copyright; psName = CTFontCopyPostScriptName(ctfont); copyright = CTFontCopyName(ctfont, kCTFontCopyrightNameKey); - result.postscriptName = QCFString::toQString(psName).toUtf8(); - result.copyright = QCFString::toQString(copyright).toUtf8(); + result.postscriptName = QString::fromCFString(psName).toUtf8(); + result.copyright = QString::fromCFString(copyright).toUtf8(); qreal emSquare = CTFontGetUnitsPerEm(ctfont); qreal scale = emSquare / CTFontGetSize(ctfont); diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h b/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h index 22008162f7..d9ffbb5697 100644 --- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h +++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h @@ -54,7 +54,7 @@ #include <private/qfontengine_p.h> #include <private/qcore_mac_p.h> -#ifndef Q_OS_IOS +#ifdef Q_OS_OSX #include <ApplicationServices/ApplicationServices.h> #else #include <CoreText/CoreText.h> @@ -78,6 +78,7 @@ public: glyph_metrics_t boundingBox(glyph_t glyph) 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; @@ -92,6 +93,9 @@ public: int synthesized() const Q_DECL_OVERRIDE { return synthesisFlags; } bool supportsSubPixelPositions() const Q_DECL_OVERRIDE { return true; } + QFixed lineThickness() const Q_DECL_OVERRIDE; + QFixed underlinePosition() const Q_DECL_OVERRIDE; + void draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight); FaceId faceId() const Q_DECL_OVERRIDE; @@ -126,6 +130,8 @@ private: int synthesisFlags; CGAffineTransform transform; QFixed avgCharWidth; + QFixed underlineThickness; + QFixed underlinePos; QFontEngine::FaceId face_id; mutable bool kerningPairsLoaded; }; diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp new file mode 100644 index 0000000000..b8d997bc35 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp @@ -0,0 +1,2007 @@ +/**************************************************************************** +** +** 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; + auto it = m_fontDatas.constFind(key); + if (it == m_fontDatas.constEnd()) + return E_FAIL; + + QByteArray fontData = it.value(); + 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 false; +} + +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..6130107cc8 --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/qwindowsfontenginedirectwrite.cpp @@ -0,0 +1,1004 @@ +/**************************************************************************** +** +** 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> +#include <QtGui/private/qhighdpiscaling_p.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) +{ + if (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting) + hintingPreference = QFont::PreferVerticalHinting; + + 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..4fc4e7b8ec --- /dev/null +++ b/src/platformsupport/fontdatabases/windows/windows.pri @@ -0,0 +1,28 @@ +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(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/fontdatabases/winrt/qwinrtfontdatabase.cpp b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp new file mode 100644 index 0000000000..f214184c36 --- /dev/null +++ b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase.cpp @@ -0,0 +1,491 @@ +/**************************************************************************** +** +** 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 "qwinrtfontdatabase_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QFile> + +#include <QtCore/QUuid> +#include <QtGui/private/qfontengine_ft_p.h> +#include <dwrite_1.h> +#include <wrl.h> +using namespace Microsoft::WRL; + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQpaFonts, "qt.qpa.fonts") + +QDebug operator<<(QDebug d, const QFontDef &def) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "Family=" << def.family << " Stylename=" << def.styleName + << " pointsize=" << def.pointSize << " pixelsize=" << def.pixelSize + << " styleHint=" << def.styleHint << " weight=" << def.weight + << " stretch=" << def.stretch << " hintingPreference=" + << def.hintingPreference; + return d; +} + +// Based on unicode range tables at http://www.microsoft.com/typography/otspec/os2.htm#ur +static QFontDatabase::WritingSystem writingSystemFromUnicodeRange(const DWRITE_UNICODE_RANGE &range) +{ + if (range.first >= 0x0000 && range.last <= 0x007F) + return QFontDatabase::Latin; + if (range.first >= 0x0370 && range.last <= 0x03FF) + return QFontDatabase::Greek; + if (range.first >= 0x0400 && range.last <= 0x04FF) + return QFontDatabase::Cyrillic; + if (range.first >= 0x0530 && range.last <= 0x058F) + return QFontDatabase::Armenian; + if (range.first >= 0x0590 && range.last <= 0x05FF) + return QFontDatabase::Hebrew; + if (range.first >= 0x0600 && range.last <= 0x06FF) + return QFontDatabase::Arabic; + if (range.first >= 0x0700 && range.last <= 0x074F) + return QFontDatabase::Syriac; + if (range.first >= 0x0780 && range.last <= 0x07BF) + return QFontDatabase::Thaana; + if (range.first >= 0x0900 && range.last <= 0x097F) + return QFontDatabase::Devanagari; + if (range.first >= 0x0980 && range.last <= 0x09FF) + return QFontDatabase::Bengali; + if (range.first >= 0x0A00 && range.last <= 0x0A7F) + return QFontDatabase::Gurmukhi; + if (range.first >= 0x0A80 && range.last <= 0x0AFF) + return QFontDatabase::Gujarati; + if (range.first >= 0x0B00 && range.last <= 0x0B7F) + return QFontDatabase::Oriya; + if (range.first >= 0x0B80 && range.last <= 0x0BFF) + return QFontDatabase::Tamil; + if (range.first >= 0x0C00 && range.last <= 0x0C7F) + return QFontDatabase::Telugu; + if (range.first >= 0x0C80 && range.last <= 0x0CFF) + return QFontDatabase::Kannada; + if (range.first >= 0x0D00 && range.last <= 0x0D7F) + return QFontDatabase::Malayalam; + if (range.first >= 0x0D80 && range.last <= 0x0DFF) + return QFontDatabase::Sinhala; + if (range.first >= 0x0E00 && range.last <= 0x0E7F) + return QFontDatabase::Thai; + if (range.first >= 0x0E80 && range.last <= 0x0EFF) + return QFontDatabase::Lao; + if (range.first >= 0x0F00 && range.last <= 0x0FFF) + return QFontDatabase::Tibetan; + if (range.first >= 0x1000 && range.last <= 0x109F) + return QFontDatabase::Myanmar; + if (range.first >= 0x10A0 && range.last <= 0x10FF) + return QFontDatabase::Georgian; + if (range.first >= 0x1780 && range.last <= 0x17FF) + return QFontDatabase::Khmer; + if (range.first >= 0x4E00 && range.last <= 0x9FFF) + return QFontDatabase::SimplifiedChinese; + if (range.first >= 0xAC00 && range.last <= 0xD7AF) + return QFontDatabase::Korean; + if (range.first >= 0x1680 && range.last <= 0x169F) + return QFontDatabase::Ogham; + if (range.first >= 0x16A0 && range.last <= 0x16FF) + return QFontDatabase::Runic; + if (range.first >= 0x07C0 && range.last <= 0x07FF) + return QFontDatabase::Nko; + + return QFontDatabase::Other; +} + +QString QWinRTFontDatabase::fontDir() const +{ + qCDebug(lcQpaFonts) << __FUNCTION__; + QString fontDirectory = QBasicFontDatabase::fontDir(); + if (!QFile::exists(fontDirectory)) { + // Fall back to app directory + fonts, and just app directory after that + const QString applicationDirPath = QCoreApplication::applicationDirPath(); + fontDirectory = applicationDirPath + QLatin1String("/fonts"); + if (!QFile::exists(fontDirectory)) { + if (m_fontFamilies.isEmpty()) + qWarning("No fonts directory found in application package."); + fontDirectory = applicationDirPath; + } + } + return fontDirectory; +} + +QWinRTFontDatabase::~QWinRTFontDatabase() +{ + qCDebug(lcQpaFonts) << __FUNCTION__; + + foreach (IDWriteFontFile *fontFile, m_fonts.keys()) + fontFile->Release(); + + foreach (IDWriteFontFamily *fontFamily, m_fontFamilies) + fontFamily->Release(); +} + +QFont QWinRTFontDatabase::defaultFont() const +{ + return QFont(QStringLiteral("Segoe UI")); +} + +bool QWinRTFontDatabase::fontsAlwaysScalable() const +{ + return true; +} + +void QWinRTFontDatabase::populateFontDatabase() +{ + qCDebug(lcQpaFonts) << __FUNCTION__; + + ComPtr<IDWriteFactory1> factory; + HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory1), &factory); + if (FAILED(hr)) { + qWarning("Failed to create DirectWrite factory: %s", qPrintable(qt_error_string(hr))); + QBasicFontDatabase::populateFontDatabase(); + return; + } + + ComPtr<IDWriteFontCollection> fontCollection; + hr = factory->GetSystemFontCollection(&fontCollection); + if (FAILED(hr)) { + qWarning("Failed to open system font collection: %s", qPrintable(qt_error_string(hr))); + QBasicFontDatabase::populateFontDatabase(); + return; + } + + int fontFamilyCount = fontCollection->GetFontFamilyCount(); + for (int i = 0; i < fontFamilyCount; ++i) { + ComPtr<IDWriteFontFamily> fontFamily; + hr = fontCollection->GetFontFamily(i, &fontFamily); + if (FAILED(hr)) { + qWarning("Unable to get font family: %s", qPrintable(qt_error_string(hr))); + continue; + } + + ComPtr<IDWriteLocalizedStrings> names; + hr = fontFamily->GetFamilyNames(&names); + if (FAILED(hr)) { + qWarning("Unable to get font family names: %s", qPrintable(qt_error_string(hr))); + continue; + } + quint32 familyNameLength; + hr = names->GetStringLength(0, &familyNameLength); + if (FAILED(hr)) { + qWarning("Unable to get family name length: %s", qPrintable(qt_error_string(hr))); + continue; + } + QVector<wchar_t> familyBuffer(familyNameLength + 1); + hr = names->GetString(0, familyBuffer.data(), familyBuffer.size()); + if (FAILED(hr)) { + qWarning("Unable to create font family name: %s", qPrintable(qt_error_string(hr))); + continue; + } + QString familyName = QString::fromWCharArray(familyBuffer.data(), familyNameLength); + + m_fontFamilies.insert(familyName, fontFamily.Detach()); + + registerFontFamily(familyName); + } + + QBasicFontDatabase::populateFontDatabase(); +} + +void QWinRTFontDatabase::populateFamily(const QString &familyName) +{ + qCDebug(lcQpaFonts) << __FUNCTION__ << familyName; + + IDWriteFontFamily *fontFamily = m_fontFamilies.value(familyName); + if (!fontFamily) { + qWarning("The font family %s was not found.", qPrintable(familyName)); + return; + } + + bool fontRegistered = false; + const int fontCount = fontFamily->GetFontCount(); + for (int j = 0; j < fontCount; ++j) { + ComPtr<IDWriteFont> font; + HRESULT hr = fontFamily->GetFont(j, &font); + if (FAILED(hr)) { + qWarning("Unable to get font: %s", qPrintable(qt_error_string(hr))); + continue; + } + + // Skip simulated faces + if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) + continue; + + ComPtr<IDWriteFontFace> baseFontFace; + hr = font->CreateFontFace(&baseFontFace); + if (FAILED(hr)) { + qWarning("Unable to create base font face: %s", qPrintable(qt_error_string(hr))); + continue; + } + ComPtr<IDWriteFontFace1> fontFace; + hr = baseFontFace.As(&fontFace); + if (FAILED(hr)) { + qWarning("Unable to create font face: %s", qPrintable(qt_error_string(hr))); + continue; + } + + // We can't deal with multi-file fonts + quint32 fileCount; + hr = fontFace->GetFiles(&fileCount, NULL); + if (FAILED(hr)) { + qWarning("Unable to get font file count: %s", qPrintable(qt_error_string(hr))); + continue; + } + if (fileCount != 1) + continue; + + ComPtr<IDWriteLocalizedStrings> informationalStrings; + BOOL exists; + hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_MANUFACTURER, + &informationalStrings, &exists); + if (FAILED(hr)) { + qWarning("Unable to get font foundry: %s", qPrintable(qt_error_string(hr))); + continue; + } + QString foundryName; + if (exists) { + quint32 length; + hr = informationalStrings->GetStringLength(0, &length); + if (FAILED(hr)) + qWarning("Unable to get foundry name length: %s", qPrintable(qt_error_string(hr))); + if (SUCCEEDED(hr)) { + QVector<wchar_t> buffer(length + 1); + hr = informationalStrings->GetString(0, buffer.data(), buffer.size()); + if (FAILED(hr)) + qWarning("Unable to get foundry name: %s", qPrintable(qt_error_string(hr))); + if (SUCCEEDED(hr)) + foundryName = QString::fromWCharArray(buffer.data(), length); + } + } + + QFont::Weight weight = QPlatformFontDatabase::weightFromInteger(font->GetWeight()); + + QFont::Style style; + switch (font->GetStyle()) { + default: + case DWRITE_FONT_STYLE_NORMAL: + style = QFont::StyleNormal; + break; + case DWRITE_FONT_STYLE_OBLIQUE: + style = QFont::StyleOblique; + break; + case DWRITE_FONT_STYLE_ITALIC: + style = QFont::StyleItalic; + break; + } + + QFont::Stretch stretch; + switch (font->GetStretch()) { + default: + case DWRITE_FONT_STRETCH_UNDEFINED: + case DWRITE_FONT_STRETCH_NORMAL: + stretch = QFont::Unstretched; + break; + case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: + stretch = QFont::UltraCondensed; + break; + case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: + stretch = QFont::ExtraCondensed; + break; + case DWRITE_FONT_STRETCH_CONDENSED: + stretch = QFont::Condensed; + break; + case DWRITE_FONT_STRETCH_SEMI_CONDENSED: + stretch = QFont::SemiCondensed; + break; + case DWRITE_FONT_STRETCH_SEMI_EXPANDED: + stretch = QFont::SemiExpanded; + break; + case DWRITE_FONT_STRETCH_EXPANDED: + stretch = QFont::Expanded; + break; + case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: + stretch = QFont::ExtraExpanded; + break; + case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: + stretch = QFont::UltraExpanded; + break; + } + + const bool fixedPitch = fontFace->IsMonospacedFont(); + + // Get writing systems from unicode ranges + quint32 actualRangeCount; + hr = fontFace->GetUnicodeRanges(0, nullptr, &actualRangeCount); + Q_ASSERT(hr == E_NOT_SUFFICIENT_BUFFER); + QVector<DWRITE_UNICODE_RANGE> unicodeRanges(actualRangeCount); + hr = fontFace->GetUnicodeRanges(actualRangeCount, unicodeRanges.data(), &actualRangeCount); + if (FAILED(hr)) { + qWarning("Unable to get font unicode range: %s", qPrintable(qt_error_string(hr))); + continue; + } + QSupportedWritingSystems writingSystems; + for (quint32 i = 0; i < actualRangeCount; ++i) { + const QFontDatabase::WritingSystem writingSystem = writingSystemFromUnicodeRange(unicodeRanges.at(i)); + writingSystems.setSupported(writingSystem); + } + if (writingSystems.supported(QFontDatabase::SimplifiedChinese)) { + writingSystems.setSupported(QFontDatabase::TraditionalChinese); + writingSystems.setSupported(QFontDatabase::Japanese); + } + if (writingSystems.supported(QFontDatabase::Latin)) + writingSystems.setSupported(QFontDatabase::Vietnamese); + + IDWriteFontFile *fontFile; + hr = fontFace->GetFiles(&fileCount, &fontFile); + if (FAILED(hr)) { + qWarning("Unable to get font file: %s", qPrintable(qt_error_string(hr))); + continue; + } + + FontDescription description = { fontFace->GetIndex(), QUuid::createUuid().toByteArray() }; + m_fonts.insert(fontFile, description); + registerFont(familyName, QString(), foundryName, weight, style, stretch, + true, true, 0, fixedPitch, writingSystems, fontFile); + fontRegistered = true; + } + + // Always populate something to avoid an assert + if (!fontRegistered) { + registerFont(familyName, QString(), QString(), QFont::Normal, QFont::StyleNormal, + QFont::Unstretched, false, false, 0, false, QSupportedWritingSystems(), 0); + } +} + +QFontEngine *QWinRTFontDatabase::fontEngine(const QFontDef &fontDef, void *handle) +{ + qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef << handle; + + if (!handle) // Happens if a font family population failed + return 0; + + IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle); + if (!m_fonts.contains(fontFile)) + return QBasicFontDatabase::fontEngine(fontDef, handle); + + const void *referenceKey; + quint32 referenceKeySize; + HRESULT hr = fontFile->GetReferenceKey(&referenceKey, &referenceKeySize); + if (FAILED(hr)) { + qWarning("Unable to get font file reference key: %s", qPrintable(qt_error_string(hr))); + return 0; + } + + ComPtr<IDWriteFontFileLoader> loader; + hr = fontFile->GetLoader(&loader); + if (FAILED(hr)) { + qWarning("Unable to get font file loader: %s", qPrintable(qt_error_string(hr))); + return 0; + } + + ComPtr<IDWriteFontFileStream> stream; + hr =loader->CreateStreamFromKey(referenceKey, referenceKeySize, &stream); + if (FAILED(hr)) { + qWarning("Unable to get font file stream: %s", qPrintable(qt_error_string(hr))); + return 0; + } + + quint64 fileSize; + hr = stream->GetFileSize(&fileSize); + if (FAILED(hr)) { + qWarning("Unable to get font file size: %s", qPrintable(qt_error_string(hr))); + return 0; + } + + const void *data; + void *context; + hr = stream->ReadFileFragment(&data, 0, fileSize, &context); + if (FAILED(hr)) { + qWarning("Unable to get font file data: %s", qPrintable(qt_error_string(hr))); + return 0; + } + const QByteArray fontData((const char *)data, fileSize); + stream->ReleaseFileFragment(context); + + QFontEngine::FaceId faceId; + const FontDescription description = m_fonts.value(fontFile); + faceId.uuid = description.uuid; + faceId.index = description.index; + const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); + QFontEngineFT::GlyphFormat format = antialias ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono; + QFontEngineFT *engine = new QFontEngineFT(fontDef); + if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) { + delete engine; + return 0; + } + + return engine; +} + +QStringList QWinRTFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, + QFont::StyleHint styleHint, + QChar::Script script) const +{ + Q_UNUSED(style) + Q_UNUSED(styleHint) + Q_UNUSED(script) + + qCDebug(lcQpaFonts) << __FUNCTION__ << family; + + QStringList result; + if (family == QLatin1String("Helvetica")) + result.append(QStringLiteral("Arial")); + result.append(QBasicFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + return result; +} + +void QWinRTFontDatabase::releaseHandle(void *handle) +{ + qCDebug(lcQpaFonts) << __FUNCTION__ << handle; + + if (!handle) + return; + + IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle); + if (m_fonts.contains(fontFile)) { + m_fonts.remove(fontFile); + fontFile->Release(); + return; + } + + QBasicFontDatabase::releaseHandle(handle); +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase_p.h b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase_p.h new file mode 100644 index 0000000000..9559bac0a8 --- /dev/null +++ b/src/platformsupport/fontdatabases/winrt/qwinrtfontdatabase_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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 QWINRTFONTDATABASE_H +#define QWINRTFONTDATABASE_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/QLoggingCategory> + +struct IDWriteFontFile; +struct IDWriteFontFamily; + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQpaFonts) + +struct FontDescription +{ + quint32 index; + QByteArray uuid; +}; + +class QWinRTFontDatabase : public QBasicFontDatabase +{ +public: + QString fontDir() const; + ~QWinRTFontDatabase(); + QFont defaultFont() const Q_DECL_OVERRIDE; + bool fontsAlwaysScalable() const Q_DECL_OVERRIDE; + void populateFontDatabase() Q_DECL_OVERRIDE; + void populateFamily(const QString &familyName) Q_DECL_OVERRIDE; + QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE; + QStringList fallbacksForFamily(const QString &family, QFont::Style style, + QFont::StyleHint styleHint, QChar::Script script) const Q_DECL_OVERRIDE; + void releaseHandle(void *handle) Q_DECL_OVERRIDE; +private: + QHash<IDWriteFontFile *, FontDescription> m_fonts; + QHash<QString, IDWriteFontFamily *> m_fontFamilies; +}; + +QT_END_NAMESPACE + +#endif // QWINRTFONTDATABASE_H diff --git a/src/platformsupport/fontdatabases/winrt/winrt.pri b/src/platformsupport/fontdatabases/winrt/winrt.pri new file mode 100644 index 0000000000..4875338182 --- /dev/null +++ b/src/platformsupport/fontdatabases/winrt/winrt.pri @@ -0,0 +1,11 @@ +QT *= gui-private + +SOURCES += \ + $$PWD/qwinrtfontdatabase.cpp + +HEADERS += \ + $$PWD/qwinrtfontdatabase_p.h + +DEFINES += __WRL_NO_DEFAULT_LIB__ + +LIBS += $$QMAKE_LIBS_CORE -ldwrite diff --git a/src/platformsupport/glxconvenience/glxconvenience.pri b/src/platformsupport/glxconvenience/glxconvenience.pri deleted file mode 100644 index cc5b198f6c..0000000000 --- a/src/platformsupport/glxconvenience/glxconvenience.pri +++ /dev/null @@ -1,8 +0,0 @@ -contains(QT_CONFIG, xlib) { - contains(QT_CONFIG,opengl):!contains(QT_CONFIG,opengles2) { - contains(QT_CONFIG, xrender): LIBS_PRIVATE += -lXrender - 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/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/graphics/qrasterbackingstore.cpp b/src/platformsupport/graphics/qrasterbackingstore.cpp index 3b1a87b8cd..ef26edc494 100644 --- a/src/platformsupport/graphics/qrasterbackingstore.cpp +++ b/src/platformsupport/graphics/qrasterbackingstore.cpp @@ -39,6 +39,7 @@ #include "qrasterbackingstore_p.h" +#include <QtGui/qbackingstore.h> #include <QtGui/qpainter.h> QT_BEGIN_NAMESPACE @@ -62,13 +63,20 @@ void QRasterBackingStore::resize(const QSize &size, const QRegion &staticContent if (m_image.size() == effectiveBufferSize) return; - QImage::Format format = window()->format().hasAlpha() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; - m_image = QImage(effectiveBufferSize, format); + m_image = QImage(effectiveBufferSize, format()); m_image.setDevicePixelRatio(windowDevicePixelRatio); - if (format == QImage::Format_ARGB32_Premultiplied) + if (m_image.format() == QImage::Format_ARGB32_Premultiplied) m_image.fill(Qt::transparent); } +QImage::Format QRasterBackingStore::format() const +{ + if (window()->format().hasAlpha()) + return QImage::Format_ARGB32_Premultiplied; + else + return QImage::Format_RGB32; +} + QPaintDevice *QRasterBackingStore::paintDevice() { return &m_image; @@ -89,7 +97,7 @@ bool QRasterBackingStore::scroll(const QRegion ®ion, int dx, int dy) const qreal devicePixelRatio = m_image.devicePixelRatio(); const QPoint delta(dx * devicePixelRatio, dy * devicePixelRatio); - foreach (const QRect &rect, region.rects()) + for (const QRect &rect : region) qt_scrollRectInImage(m_image, QRect(rect.topLeft() * devicePixelRatio, rect.size() * devicePixelRatio), delta); return true; @@ -97,13 +105,17 @@ bool QRasterBackingStore::scroll(const QRegion ®ion, int dx, int dy) void QRasterBackingStore::beginPaint(const QRegion ®ion) { + // Keep backing store device pixel ratio in sync with window + if (m_image.devicePixelRatio() != window()->devicePixelRatio()) + resize(backingStore()->size(), backingStore()->staticContents()); + if (!m_image.hasAlphaChannel()) return; QPainter painter(&m_image); painter.setCompositionMode(QPainter::CompositionMode_Source); const QColor blank = Qt::transparent; - foreach (const QRect &rect, region.rects()) + for (const QRect &rect : region) painter.fillRect(rect, blank); } diff --git a/src/platformsupport/graphics/qrasterbackingstore_p.h b/src/platformsupport/graphics/qrasterbackingstore_p.h index de96c99b5a..55976d2ceb 100644 --- a/src/platformsupport/graphics/qrasterbackingstore_p.h +++ b/src/platformsupport/graphics/qrasterbackingstore_p.h @@ -62,13 +62,16 @@ public: QRasterBackingStore(QWindow *window); ~QRasterBackingStore(); - QPaintDevice *paintDevice() Q_DECL_OVERRIDE; - QImage toImage() const Q_DECL_OVERRIDE; - void resize (const QSize &size, const QRegion &) Q_DECL_OVERRIDE; + void resize(const QSize &size, const QRegion &staticContents) Q_DECL_OVERRIDE; bool scroll(const QRegion &area, int dx, int dy) Q_DECL_OVERRIDE; void beginPaint(const QRegion ®ion) Q_DECL_OVERRIDE; + QPaintDevice *paintDevice() Q_DECL_OVERRIDE; + QImage toImage() const Q_DECL_OVERRIDE; + protected: + virtual QImage::Format format() const; + QImage m_image; }; diff --git a/src/platformsupport/input/evdevkeyboard/evdevkeyboard.pri b/src/platformsupport/input/evdevkeyboard/evdevkeyboard.pri index c058498ba0..445f230fa5 100644 --- a/src/platformsupport/input/evdevkeyboard/evdevkeyboard.pri +++ b/src/platformsupport/input/evdevkeyboard/evdevkeyboard.pri @@ -7,6 +7,5 @@ SOURCES += \ $$PWD/qevdevkeyboardhandler.cpp \ $$PWD/qevdevkeyboardmanager.cpp -contains(QT_CONFIG, libudev) { - LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV -} +qtConfig(libudev): \ + QMAKE_USE_PRIVATE += libudev diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp index ffe9f82325..94ebae8f42 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp @@ -84,10 +84,9 @@ QEvdevKeyboardManager::QEvdevKeyboardManager(const QString &key, const QString & m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Keyboard, this); if (m_deviceDiscovery) { // scan and add already connected keyboards - QStringList devices = m_deviceDiscovery->scanConnectedDevices(); - foreach (const QString &device, devices) { + const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + for (const QString &device : devices) addKeyboard(device); - } connect(m_deviceDiscovery, SIGNAL(deviceDetected(QString)), this, SLOT(addKeyboard(QString))); connect(m_deviceDiscovery, SIGNAL(deviceRemoved(QString)), this, SLOT(removeKeyboard(QString))); 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/evdevmouse.pri b/src/platformsupport/input/evdevmouse/evdevmouse.pri index 7e7a9e1629..a8c9c663f3 100644 --- a/src/platformsupport/input/evdevmouse/evdevmouse.pri +++ b/src/platformsupport/input/evdevmouse/evdevmouse.pri @@ -6,7 +6,6 @@ SOURCES += \ $$PWD/qevdevmousehandler.cpp \ $$PWD/qevdevmousemanager.cpp -contains(QT_CONFIG, libudev) { - LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV -} +qtConfig(libudev): \ + QMAKE_USE_PRIVATE += libudev diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp index 129654c99b..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> @@ -90,10 +90,9 @@ QEvdevMouseManager::QEvdevMouseManager(const QString &key, const QString &specif m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Mouse | QDeviceDiscovery::Device_Touchpad, this); if (m_deviceDiscovery) { // scan and add already connected keyboards - QStringList devices = m_deviceDiscovery->scanConnectedDevices(); - foreach (const QString &device, devices) { + const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + for (const QString &device : devices) addMouse(device); - } connect(m_deviceDiscovery, SIGNAL(deviceDetected(QString)), this, SLOT(addMouse(QString))); connect(m_deviceDiscovery, SIGNAL(deviceRemoved(QString)), this, SLOT(removeMouse(QString))); diff --git a/src/platformsupport/input/evdevtablet/evdevtablet.pri b/src/platformsupport/input/evdevtablet/evdevtablet.pri index fb9489353c..ea1712ad2f 100644 --- a/src/platformsupport/input/evdevtablet/evdevtablet.pri +++ b/src/platformsupport/input/evdevtablet/evdevtablet.pri @@ -6,6 +6,5 @@ SOURCES += \ $$PWD/qevdevtablethandler.cpp \ $$PWD/qevdevtabletmanager.cpp -contains(QT_CONFIG, libudev) { - LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV -} +qtConfig(libudev): \ + QMAKE_USE_PRIVATE += libudev diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp index 00a8c7a65d..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> @@ -85,8 +85,8 @@ QEvdevTabletManager::QEvdevTabletManager(const QString &key, const QString &spec qCDebug(qLcEvdevTablet) << "evdevtablet: Using device discovery"; m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this); if (m_deviceDiscovery) { - QStringList devices = m_deviceDiscovery->scanConnectedDevices(); - foreach (const QString &device, devices) + const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + for (const QString &device : devices) addDevice(device); connect(m_deviceDiscovery, SIGNAL(deviceDetected(QString)), this, SLOT(addDevice(QString))); connect(m_deviceDiscovery, SIGNAL(deviceRemoved(QString)), this, SLOT(removeDevice(QString))); diff --git a/src/platformsupport/input/evdevtouch/evdevtouch.pri b/src/platformsupport/input/evdevtouch/evdevtouch.pri index c2edc13143..0ad236e882 100644 --- a/src/platformsupport/input/evdevtouch/evdevtouch.pri +++ b/src/platformsupport/input/evdevtouch/evdevtouch.pri @@ -6,12 +6,11 @@ SOURCES += \ $$PWD/qevdevtouchhandler.cpp \ $$PWD/qevdevtouchmanager.cpp -contains(QT_CONFIG, libudev) { - LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV -} +INCLUDEPATH += $$PWD/../shared -contains(QT_CONFIG, mtdev) { - CONFIG += link_pkgconfig - PKGCONFIG_PRIVATE += mtdev -} +qtConfig(libudev): \ + QMAKE_USE_PRIVATE += libudev +qtConfig(mtdev) { + QMAKE_USE_PRIVATE += mtdev +} diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp index 435f411f96..d53a317fc5 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qevdevtouchhandler_p.h" +#include "qtouchoutputmapping_p.h" #include <QStringList> #include <QHash> #include <QSocketNotifier> @@ -49,7 +50,7 @@ #include <QtGui/private/qguiapplication_p.h> #include <linux/input.h> -#if !defined(QT_NO_MTDEV) +#if QT_CONFIG(mtdev) extern "C" { #include <mtdev.h> } @@ -116,6 +117,7 @@ public: int findClosestContact(const QHash<int, Contact> &contacts, int x, int y, int *dist); void addTouchPoint(const Contact &contact, Qt::TouchPointStates *combinedStates); void reportPoints(); + void loadMultiScreenMappings(); int hw_range_x_min; int hw_range_x_max; @@ -124,10 +126,13 @@ public: int hw_pressure_min; int hw_pressure_max; QString hw_name; + QString deviceNode; bool m_forceToActiveWindow; bool m_typeB; QTransform m_rotate; bool m_singleTouch; + QString m_screenName; + QPointer<QScreen> m_screen; }; QEvdevTouchScreenData::QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr, const QStringList &args) @@ -145,7 +150,7 @@ QEvdevTouchScreenData::QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr, co #define LONG_BITS (sizeof(long) << 3) #define NUM_LONGS(bits) (((bits) + LONG_BITS - 1) / LONG_BITS) -#if defined(QT_NO_MTDEV) +#if !QT_CONFIG(mtdev) static inline bool testBit(long bit, const long *array) { return (array[bit / LONG_BITS] >> bit % LONG_BITS) & 1; @@ -154,7 +159,7 @@ static inline bool testBit(long bit, const long *array) QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const QString &spec, QObject *parent) : QObject(parent), m_notify(Q_NULLPTR), m_fd(-1), d(Q_NULLPTR), m_device(Q_NULLPTR) -#if !defined(QT_NO_MTDEV) +#if QT_CONFIG(mtdev) , m_mtdev(Q_NULLPTR) #endif { @@ -198,7 +203,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const return; } -#if !defined(QT_NO_MTDEV) +#if QT_CONFIG(mtdev) m_mtdev = static_cast<mtdev *>(calloc(1, sizeof(mtdev))); int mtdeverr = mtdev_open(m_mtdev, m_fd); if (mtdeverr) { @@ -210,7 +215,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const d = new QEvdevTouchScreenData(this, args); -#if !defined(QT_NO_MTDEV) +#if QT_CONFIG(mtdev) const char *mtdevStr = "(mtdev)"; d->m_typeB = true; #else @@ -222,7 +227,9 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const } #endif - qCDebug(qLcEvdevTouch, "evdevtouch: %s: Protocol type %c %s (%s)", qPrintable(device), + d->deviceNode = device; + + qCDebug(qLcEvdevTouch, "evdevtouch: %s: Protocol type %c %s (%s)", qPrintable(d->deviceNode), d->m_typeB ? 'B' : 'A', mtdevStr, d->m_singleTouch ? "single" : "multi"); input_absinfo absInfo; @@ -292,12 +299,20 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const if (inverty) d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(1.0, -1.0).translate(-0.5, -0.5); + QTouchOutputMapping mapping; + if (mapping.load()) { + d->m_screenName = mapping.screenNameForDeviceNode(d->deviceNode); + if (!d->m_screenName.isEmpty()) + qCDebug(qLcEvdevTouch, "evdevtouch: Mapping device %s to screen %s", + qPrintable(d->deviceNode), qPrintable(d->m_screenName)); + } + registerTouchDevice(); } QEvdevTouchScreenHandler::~QEvdevTouchScreenHandler() { -#if !defined(QT_NO_MTDEV) +#if QT_CONFIG(mtdev) if (m_mtdev) { mtdev_close(m_mtdev); free(m_mtdev); @@ -322,7 +337,7 @@ void QEvdevTouchScreenHandler::readData() ::input_event buffer[32]; int events = 0; -#if !defined(QT_NO_MTDEV) +#if QT_CONFIG(mtdev) forever { do { events = mtdev_get(m_mtdev, m_fd, buffer, sizeof(buffer) / sizeof(::input_event)); @@ -647,8 +662,31 @@ void QEvdevTouchScreenData::reportPoints() return; winRect = QHighDpi::toNativePixels(win->geometry(), win); } else { - QScreen *primary = QGuiApplication::primaryScreen(); - winRect = QHighDpi::toNativePixels(primary->geometry(), primary); + // Now it becomes tricky. Traditionally we picked the primaryScreen() + // and were done with it. But then, enter multiple screens, and + // suddenly it was all broken. + // + // For now we only support the display configuration of the KMS/DRM + // backends of eglfs. See QTouchOutputMapping. + // + // The good news it that once winRect refers to the correct screen + // geometry in the full virtual desktop space, there is nothing else + // left to do since qguiapp will handle the rest. + QScreen *screen = QGuiApplication::primaryScreen(); + if (!m_screenName.isEmpty()) { + if (!m_screen) { + const QList<QScreen *> screens = QGuiApplication::screens(); + for (QScreen *s : screens) { + if (s->name() == m_screenName) { + m_screen = s; + break; + } + } + } + if (m_screen) + screen = m_screen; + } + winRect = QHighDpi::toNativePixels(screen->geometry(), screen); } const int hw_w = hw_range_x_max - hw_range_x_min; @@ -679,10 +717,12 @@ void QEvdevTouchScreenData::reportPoints() tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min); } + // Let qguiapp pick the target window. QWindowSystemInterface::handleTouchEvent(Q_NULLPTR, q->touchDevice(), m_touchPoints); } + QEvdevTouchScreenHandlerThread::QEvdevTouchScreenHandlerThread(const QString &device, const QString &spec, QObject *parent) : QDaemonThread(parent), m_device(device), m_spec(spec), m_handler(Q_NULLPTR), m_touchDeviceRegistered(false) { diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler_p.h b/src/platformsupport/input/evdevtouch/qevdevtouchhandler_p.h index a423bbd2ef..6554d4998c 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler_p.h +++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler_p.h @@ -51,6 +51,7 @@ // We mean it. // +#include <QtGui/private/qtguiglobal_p.h> #include <QObject> #include <QString> #include <QList> @@ -58,7 +59,7 @@ #include <QtCore/private/qthread_p.h> #include <qpa/qwindowsysteminterface.h> -#if !defined(QT_NO_MTDEV) +#if QT_CONFIG(mtdev) struct mtdev; #endif @@ -88,7 +89,7 @@ private: int m_fd; QEvdevTouchScreenData *d; QTouchDevice *m_device; -#if !defined(QT_NO_MTDEV) +#if QT_CONFIG(mtdev) mtdev *m_mtdev; #endif }; diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp index 2de0816ef5..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> @@ -85,8 +85,8 @@ QEvdevTouchManager::QEvdevTouchManager(const QString &key, const QString &specif qCDebug(qLcEvdevTouch) << "evdevtouch: Using device discovery"; m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Touchpad | QDeviceDiscovery::Device_Touchscreen, this); if (m_deviceDiscovery) { - QStringList devices = m_deviceDiscovery->scanConnectedDevices(); - foreach (const QString &device, devices) + const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + for (const QString &device : devices) addDevice(device); connect(m_deviceDiscovery, SIGNAL(deviceDetected(QString)), this, SLOT(addDevice(QString))); connect(m_deviceDiscovery, SIGNAL(deviceRemoved(QString)), this, SLOT(removeDevice(QString))); diff --git a/src/platformsupport/input/input.pri b/src/platformsupport/input/input.pri deleted file mode 100644 index 3b9593eb31..0000000000 --- a/src/platformsupport/input/input.pri +++ /dev/null @@ -1,14 +0,0 @@ -contains(QT_CONFIG, evdev) { - include($$PWD/evdevmouse/evdevmouse.pri) - include($$PWD/evdevkeyboard/evdevkeyboard.pri) - include($$PWD/evdevtouch/evdevtouch.pri) - include($$PWD/evdevtablet/evdevtablet.pri) -} - -contains(QT_CONFIG, tslib) { - include($$PWD/tslib/tslib.pri) -} - -contains(QT_CONFIG, libinput) { - include($$PWD/libinput/libinput.pri) -} diff --git a/src/platformsupport/input/input.pro b/src/platformsupport/input/input.pro new file mode 100644 index 0000000000..2c2ace6780 --- /dev/null +++ b/src/platformsupport/input/input.pro @@ -0,0 +1,29 @@ +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) + include($$PWD/evdevtouch/evdevtouch.pri) + include($$PWD/evdevtablet/evdevtablet.pri) +} + +qtConfig(tslib) { + include($$PWD/tslib/tslib.pri) +} + +qtConfig(libinput) { + include($$PWD/libinput/libinput.pri) +} + +qtConfig(evdev)|qtConfig(libinput) { + include($$PWD/shared/shared.pri) +} + +load(qt_module) diff --git a/src/platformsupport/input/libinput/libinput.pri b/src/platformsupport/input/libinput/libinput.pri index 35d962ff3c..f922769a37 100644 --- a/src/platformsupport/input/libinput/libinput.pri +++ b/src/platformsupport/input/libinput/libinput.pri @@ -10,14 +10,11 @@ SOURCES += \ $$PWD/qlibinputkeyboard.cpp \ $$PWD/qlibinputtouch.cpp -INCLUDEPATH += $$QMAKE_INCDIR_LIBUDEV $$QMAKE_INCDIR_LIBINPUT -LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV $$QMAKE_LIBS_LIBINPUT +QMAKE_USE_PRIVATE += libudev libinput -contains(QT_CONFIG, xkbcommon-evdev) { - INCLUDEPATH += $$QMAKE_INCDIR_XKBCOMMON_EVDEV - LIBS_PRIVATE += $$QMAKE_LIBS_XKBCOMMON_EVDEV -} else { - DEFINES += QT_NO_XKBCOMMON_EVDEV -} +INCLUDEPATH += $$PWD/../shared -DEFINES += QT_LIBINPUT_VERSION_MAJOR=$$QMAKE_LIBINPUT_VERSION_MAJOR QT_LIBINPUT_VERSION_MINOR=$$QMAKE_LIBINPUT_VERSION_MINOR +qtConfig(xkbcommon-evdev): \ + QMAKE_USE_PRIVATE += xkbcommon_evdev +else: \ + DEFINES += QT_NO_XKBCOMMON_EVDEV diff --git a/src/platformsupport/input/libinput/qlibinputpointer.cpp b/src/platformsupport/input/libinput/qlibinputpointer.cpp index d5a3a8b437..bdeac8db7e 100644 --- a/src/platformsupport/input/libinput/qlibinputpointer.cpp +++ b/src/platformsupport/input/libinput/qlibinputpointer.cpp @@ -96,7 +96,7 @@ void QLibInputPointer::processMotion(libinput_event_pointer *e) void QLibInputPointer::processAxis(libinput_event_pointer *e) { -#if QT_LIBINPUT_VERSION_MAJOR == 0 && QT_LIBINPUT_VERSION_MINOR <= 7 +#if !QT_CONFIG(libinput_axis_api) const double v = libinput_event_pointer_get_axis_value(e) * 120; const Qt::Orientation ori = libinput_event_pointer_get_axis(e) == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL ? Qt::Vertical : Qt::Horizontal; diff --git a/src/platformsupport/input/libinput/qlibinputtouch.cpp b/src/platformsupport/input/libinput/qlibinputtouch.cpp index 0a0e9daccb..42925a18e1 100644 --- a/src/platformsupport/input/libinput/qlibinputtouch.cpp +++ b/src/platformsupport/input/libinput/qlibinputtouch.cpp @@ -41,6 +41,7 @@ #include <libinput.h> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> +#include <QtGui/private/qhighdpiscaling_p.h> QT_BEGIN_NAMESPACE @@ -63,10 +64,14 @@ QLibInputTouch::DeviceState *QLibInputTouch::deviceState(libinput_event_touch *e static inline QPointF getPos(libinput_event_touch *e) { - const QSize screenSize = QGuiApplication::primaryScreen()->geometry().size(); - const double x = libinput_event_touch_get_x_transformed(e, screenSize.width()); - const double y = libinput_event_touch_get_y_transformed(e, screenSize.height()); - return QPointF(x, y); + // TODO Map to correct screen using QTouchOutputMapping. + // Perhaps investigate libinput_device_get_output_name as well. + // For now just use the primary screen. + QScreen *screen = QGuiApplication::primaryScreen(); + const QRect geom = QHighDpi::toNativePixels(screen->geometry(), screen); + const double x = libinput_event_touch_get_x_transformed(e, geom.width()); + const double y = libinput_event_touch_get_y_transformed(e, geom.height()); + return geom.topLeft() + QPointF(x, y); } void QLibInputTouch::registerDevice(libinput_device *dev) diff --git a/src/platformsupport/input/shared/qtouchoutputmapping.cpp b/src/platformsupport/input/shared/qtouchoutputmapping.cpp new file mode 100644 index 0000000000..0a1afd4739 --- /dev/null +++ b/src/platformsupport/input/shared/qtouchoutputmapping.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 "qtouchoutputmapping_p.h" +#include <QFile> +#include <QVariantMap> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +QT_BEGIN_NAMESPACE + +bool QTouchOutputMapping::load() +{ + static QByteArray configFile = qgetenv("QT_QPA_EGLFS_KMS_CONFIG"); + if (configFile.isEmpty()) + return false; + + QFile file(QString::fromUtf8(configFile)); + if (!file.open(QFile::ReadOnly)) { + qWarning("touch input support: Failed to open %s", configFile.constData()); + return false; + } + + const QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + if (!doc.isObject()) { + qWarning("touch input support: Failed to parse %s", configFile.constData()); + return false; + } + + // What we are interested is the virtualIndex and touchDevice properties for + // each element in the outputs array. + const QJsonArray outputs = doc.object().value(QLatin1String("outputs")).toArray(); + for (int i = 0; i < outputs.size(); ++i) { + const QVariantMap output = outputs.at(i).toObject().toVariantMap(); + if (!output.contains(QStringLiteral("touchDevice"))) + continue; + if (!output.contains(QStringLiteral("name"))) { + qWarning("evdevtouch: Output %d specifies touchDevice but not name, this is wrong", i); + continue; + } + const QString &deviceNode = output.value(QStringLiteral("touchDevice")).toString(); + const QString &screenName = output.value(QStringLiteral("name")).toString(); + m_screenTable.insert(deviceNode, screenName); + } + + return true; +} + +QString QTouchOutputMapping::screenNameForDeviceNode(const QString &deviceNode) +{ + return m_screenTable.value(deviceNode); +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/input/shared/qtouchoutputmapping_p.h b/src/platformsupport/input/shared/qtouchoutputmapping_p.h new file mode 100644 index 0000000000..94d4dbc3b1 --- /dev/null +++ b/src/platformsupport/input/shared/qtouchoutputmapping_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 QTOUCHOUTPUTMAPPING_P_H +#define QTOUCHOUTPUTMAPPING_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QString> +#include <QHash> + +QT_BEGIN_NAMESPACE + +class QTouchOutputMapping +{ +public: + bool load(); + QString screenNameForDeviceNode(const QString &deviceNode); + +private: + QHash<QString, QString> m_screenTable; +}; + +QT_END_NAMESPACE + +#endif // QTOUCHOUTPUTMAPPING_P_H diff --git a/src/platformsupport/input/shared/shared.pri b/src/platformsupport/input/shared/shared.pri new file mode 100644 index 0000000000..1443235244 --- /dev/null +++ b/src/platformsupport/input/shared/shared.pri @@ -0,0 +1,5 @@ +HEADERS += \ + $$PWD/qtouchoutputmapping_p.h + +SOURCES += \ + $$PWD/qtouchoutputmapping.cpp diff --git a/src/platformsupport/input/tslib/tslib.pri b/src/platformsupport/input/tslib/tslib.pri index c9fef1ca8f..9efd126e13 100644 --- a/src/platformsupport/input/tslib/tslib.pri +++ b/src/platformsupport/input/tslib/tslib.pri @@ -4,4 +4,4 @@ HEADERS += \ SOURCES += \ $$PWD/qtslib.cpp -LIBS += -lts +QMAKE_USE += tslib 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 8366c56a09..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" /*! @@ -778,9 +778,8 @@ void AtSpiAdaptor::updateEventListeners() QDBusReply<QSpiEventListenerArray> listenersReply = m_dbus->connection().call(m); if (listenersReply.isValid()) { const QSpiEventListenerArray evList = listenersReply.value(); - Q_FOREACH (const QSpiEventListener &ev, evList) { + for (const QSpiEventListener &ev : evList) setBitFlag(ev.eventName); - } m_applicationAdaptor->sendEvents(!evList.isEmpty()); } else { qAtspiDebug("Could not query active accessibility event listeners."); @@ -1508,11 +1507,10 @@ QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface) QSpiRelationArray AtSpiAdaptor::relationSet(QAccessibleInterface *interface, const QDBusConnection &connection) const { typedef QPair<QAccessibleInterface*, QAccessible::Relation> RelationPair; - QVector<RelationPair> relationInterfaces; - relationInterfaces = interface->relations(); + const QVector<RelationPair> relationInterfaces = interface->relations(); QSpiRelationArray relations; - Q_FOREACH (const RelationPair &pair, relationInterfaces) { + for (const RelationPair &pair : relationInterfaces) { // FIXME: this loop seems a bit strange... "related" always have one item when we check. //And why is it a list, when it always have one item? And it seems to assume that the QAccessible::Relation enum maps directly to AtSpi QSpiObjectReferenceArray related; @@ -1757,24 +1755,20 @@ QSpiActionArray AtSpiAdaptor::getActions(QAccessibleInterface *interface) const QSpiActionArray actions; const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); actions.reserve(actionNames.size()); - Q_FOREACH (const QString &actionName, actionNames) { + for (const QString &actionName : actionNames) { QSpiAction action; - QStringList keyBindings; action.name = actionName; if (actionInterface) { action.description = actionInterface->localizedActionDescription(actionName); - keyBindings = actionInterface->keyBindingsForAction(actionName); + const QStringList keyBindings = actionInterface->keyBindingsForAction(actionName); + if (!keyBindings.isEmpty()) + action.keyBinding = keyBindings.front(); } else { action.description = qAccessibleLocalizedActionDescription(actionName); } - if (keyBindings.length() > 0) - action.keyBinding = keyBindings[0]; - else - action.keyBinding = QString(); - - actions << action; + actions.append(std::move(action)); } return actions; } 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/dbusconnection.cpp b/src/platformsupport/linuxaccessibility/dbusconnection.cpp index a3c2ecc084..3e2248a018 100644 --- a/src/platformsupport/linuxaccessibility/dbusconnection.cpp +++ b/src/platformsupport/linuxaccessibility/dbusconnection.cpp @@ -49,7 +49,7 @@ #include "bus_interface.h" #include <QtGui/qguiapplication.h> -#include <qplatformnativeinterface.h> +#include <qpa/qplatformnativeinterface.h> QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/linuxaccessibility/linuxaccessibility.pri b/src/platformsupport/linuxaccessibility/linuxaccessibility.pri deleted file mode 100644 index 1b65fb1cad..0000000000 --- a/src/platformsupport/linuxaccessibility/linuxaccessibility.pri +++ /dev/null @@ -1,25 +0,0 @@ -contains(QT_CONFIG, 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 923d7225da..0000000000 --- a/src/platformsupport/platformcompositor/platformcompositor.pri +++ /dev/null @@ -1,7 +0,0 @@ -contains(QT_CONFIG, 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/platformcompositor/qopenglcompositor.cpp b/src/platformsupport/platformcompositor/qopenglcompositor.cpp index 610619a4e4..0f4946f81a 100644 --- a/src/platformsupport/platformcompositor/qopenglcompositor.cpp +++ b/src/platformsupport/platformcompositor/qopenglcompositor.cpp @@ -40,7 +40,6 @@ #include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLFramebufferObject> #include <QtGui/QWindow> -#include <QtGui/QMatrix4x4> #include <qpa/qplatformbackingstore.h> #include "qopenglcompositor_p.h" @@ -78,7 +77,8 @@ static QOpenGLCompositor *compositor = 0; QOpenGLCompositor::QOpenGLCompositor() : m_context(0), - m_targetWindow(0) + m_targetWindow(0), + m_rotation(0) { Q_ASSERT(!compositor); m_updateTimer.setSingleShot(true); @@ -93,10 +93,19 @@ QOpenGLCompositor::~QOpenGLCompositor() compositor = 0; } -void QOpenGLCompositor::setTarget(QOpenGLContext *context, QWindow *targetWindow) +void QOpenGLCompositor::setTarget(QOpenGLContext *context, QWindow *targetWindow, + const QRect &nativeTargetGeometry) { m_context = context; m_targetWindow = targetWindow; + m_nativeTargetGeometry = nativeTargetGeometry; +} + +void QOpenGLCompositor::setRotation(int degrees) +{ + m_rotation = degrees; + m_rotationMatrix.setToIdentity(); + m_rotationMatrix.rotate(degrees, 0, 0, 1); } void QOpenGLCompositor::update() @@ -109,7 +118,7 @@ QImage QOpenGLCompositor::grab() { Q_ASSERT(m_context && m_targetWindow); m_context->makeCurrent(m_targetWindow); - QScopedPointer<QOpenGLFramebufferObject> fbo(new QOpenGLFramebufferObject(m_targetWindow->geometry().size())); + QScopedPointer<QOpenGLFramebufferObject> fbo(new QOpenGLFramebufferObject(m_nativeTargetGeometry.size())); renderAll(fbo.data()); return fbo->toImage(); } @@ -127,9 +136,7 @@ void QOpenGLCompositor::renderAll(QOpenGLFramebufferObject *fbo) fbo->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - const QRect targetWindowRect(QPoint(0, 0), m_targetWindow->geometry().size()); - glViewport(0, 0, targetWindowRect.width(), targetWindowRect.height()); + glViewport(0, 0, m_nativeTargetGeometry.width(), m_nativeTargetGeometry.height()); if (!m_blitter.isCreated()) m_blitter.create(); @@ -181,7 +188,8 @@ static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight) topLeftRect.width(), topLeftRect.height()); } -static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRect &targetWindowRect, QOpenGLTextureBlitter *blitter) +static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRect &targetWindowRect, + QOpenGLTextureBlitter *blitter, QMatrix4x4 *rotationMatrix) { const QRect clipRect = textures->clipRect(idx); if (clipRect.isEmpty()) @@ -191,7 +199,10 @@ static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRe const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft()); const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height()); - const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(clippedRectInWindow, targetWindowRect); + QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(clippedRectInWindow, targetWindowRect); + if (rotationMatrix) + target = *rotationMatrix * target; + const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect, rectInWindow.size(), QOpenGLTextureBlitter::OriginBottomLeft); @@ -219,25 +230,29 @@ void QOpenGLCompositor::render(QOpenGLCompositorWindow *window) if (textures->count() > 1 && i == textures->count() - 1) { // Backingstore for a widget with QOpenGLWidget subwidgets blend.set(true); - const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); + QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); + if (m_rotation) + target = m_rotationMatrix * target; m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft); } else if (textures->count() == 1) { // A regular QWidget window const bool translucent = window->sourceWindow()->requestedFormat().alphaBufferSize() > 0; blend.set(translucent); - const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); + QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); + if (m_rotation) + target = m_rotationMatrix * target; m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft); } else if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) { // Texture from an FBO belonging to a QOpenGLWidget blend.set(false); - clippedBlit(textures, i, targetWindowRect, &m_blitter); + clippedBlit(textures, i, targetWindowRect, &m_blitter, m_rotation ? &m_rotationMatrix : nullptr); } } for (int i = 0; i < textures->count(); ++i) { if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) { blend.set(true); - clippedBlit(textures, i, targetWindowRect, &m_blitter); + clippedBlit(textures, i, targetWindowRect, &m_blitter, m_rotation ? &m_rotationMatrix : nullptr); } } diff --git a/src/platformsupport/platformcompositor/qopenglcompositor_p.h b/src/platformsupport/platformcompositor/qopenglcompositor_p.h index 2c34fba409..dece41f676 100644 --- a/src/platformsupport/platformcompositor/qopenglcompositor_p.h +++ b/src/platformsupport/platformcompositor/qopenglcompositor_p.h @@ -52,7 +52,8 @@ // #include <QtCore/QTimer> -#include <QtGui/private/qopengltextureblitter_p.h> +#include <QtGui/QOpenGLTextureBlitter> +#include <QtGui/QMatrix4x4> QT_BEGIN_NAMESPACE @@ -78,7 +79,8 @@ public: static QOpenGLCompositor *instance(); static void destroy(); - void setTarget(QOpenGLContext *context, QWindow *window); + void setTarget(QOpenGLContext *context, QWindow *window, const QRect &nativeTargetGeometry); + void setRotation(int degrees); QOpenGLContext *context() const { return m_context; } QWindow *targetWindow() const { return m_targetWindow; } @@ -106,6 +108,9 @@ private: QOpenGLContext *m_context; QWindow *m_targetWindow; + QRect m_nativeTargetGeometry; + int m_rotation; + QMatrix4x4 m_rotationMatrix; QTimer m_updateTimer; QOpenGLTextureBlitter m_blitter; QList<QOpenGLCompositorWindow *> m_windows; diff --git a/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp b/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp index 7c29be7804..dbaaf524e8 100644 --- a/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp +++ b/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp @@ -140,7 +140,7 @@ void QOpenGLCompositorBackingStore::updateTexture() QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { - foreach (const QRect &rect, m_dirty.rects()) { + for (const QRect &rect : m_dirty) { QRect r = imageRect & rect; glPixelStorei(GL_UNPACK_ROW_LENGTH, m_image.width()); glTexSubImage2D(GL_TEXTURE_2D, 0, r.x(), r.y(), r.width(), r.height(), GL_RGBA, GL_UNSIGNED_BYTE, @@ -148,7 +148,7 @@ void QOpenGLCompositorBackingStore::updateTexture() glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } } else { - foreach (const QRect &rect, m_dirty.rects()) { + for (const QRect &rect : m_dirty) { // intersect with image rect to be sure QRect r = imageRect & rect; @@ -161,7 +161,7 @@ void QOpenGLCompositorBackingStore::updateTexture() fixed |= r; } - foreach (const QRect &rect, fixed.rects()) { + for (const QRect &rect : fixed) { // if the sub-rect is full-width we can pass the image data directly to // OpenGL instead of copying, since there's no gap between scanlines if (rect.width() == imageRect.width()) { @@ -258,7 +258,7 @@ void QOpenGLCompositorBackingStore::beginPaint(const QRegion ®ion) if (m_image.hasAlphaChannel()) { QPainter p(&m_image); p.setCompositionMode(QPainter::CompositionMode_Source); - foreach (const QRect &r, region.rects()) + for (const QRect &r : region) p.fillRect(r, Qt::transparent); } } diff --git a/src/platformsupport/platformsupport.pro b/src/platformsupport/platformsupport.pro index 60be964b74..09e2922505 100644 --- a/src/platformsupport/platformsupport.pro +++ b/src/platformsupport/platformsupport.pro @@ -1,34 +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)|if(darwin:!if(watchos:CONFIG(simulator, simulator|device)))|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 +} + +unix:!darwin: \ + SUBDIRS += services -# dbus convenience, but not for darwin: the platform -# plugins for these platforms do not use dbus and we -# don't want to create a false dependency. -!darwin: contains(QT_CONFIG, dbus) { - include(dbusmenu/dbusmenu.pri) - include(dbustray/dbustray.pri) +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 + } } -ios: include(graphics/graphics.pri) -load(qt_module) +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/dbusmenu/dbusmenu.pri b/src/platformsupport/themes/genericunix/dbusmenu/dbusmenu.pri index 2d0feca1a2..662ebfdae6 100644 --- a/src/platformsupport/dbusmenu/dbusmenu.pri +++ b/src/platformsupport/themes/genericunix/dbusmenu/dbusmenu.pri @@ -1,6 +1,6 @@ QT_FOR_PRIVATE += dbus -INCLUDEPATH += $$PWD $$PWD/../../gui/kernel +INCLUDEPATH += $$PWD HEADERS += \ $$PWD/qdbusmenuadaptor_p.h \ diff --git a/src/platformsupport/dbusmenu/qdbusmenuadaptor.cpp b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuadaptor.cpp index f6a5144e2b..354b9c3a2e 100644 --- a/src/platformsupport/dbusmenu/qdbusmenuadaptor.cpp +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuadaptor.cpp @@ -105,7 +105,7 @@ QList<int> QDBusMenuAdaptor::AboutToShowGroup(const QList<int> &ids, QList<int> qCDebug(qLcMenu) << ids; Q_UNUSED(idErrors) idErrors.clear(); - Q_FOREACH (int id, ids) + for (int id : ids) AboutToShow(id); return QList<int>(); // updatesNeeded } @@ -134,7 +134,7 @@ void QDBusMenuAdaptor::Event(int id, const QString &eventId, const QDBusVariant QList<int> QDBusMenuAdaptor::EventGroup(const QDBusMenuEventList &events) { - Q_FOREACH (const QDBusMenuEvent &ev, events) + for (const QDBusMenuEvent &ev : events) Event(ev.m_id, ev.m_eventId, ev.m_data, ev.m_timestamp); return QList<int>(); // idErrors } diff --git a/src/platformsupport/dbusmenu/qdbusmenuadaptor_p.h b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuadaptor_p.h index 6612f019a7..6612f019a7 100644 --- a/src/platformsupport/dbusmenu/qdbusmenuadaptor_p.h +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuadaptor_p.h diff --git a/src/platformsupport/dbusmenu/qdbusmenubar.cpp b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenubar.cpp index 76d658f51a..76d658f51a 100644 --- a/src/platformsupport/dbusmenu/qdbusmenubar.cpp +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenubar.cpp diff --git a/src/platformsupport/dbusmenu/qdbusmenubar_p.h b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenubar_p.h index 8266a395f5..8266a395f5 100644 --- a/src/platformsupport/dbusmenu/qdbusmenubar_p.h +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenubar_p.h diff --git a/src/platformsupport/dbusmenu/qdbusmenuconnection.cpp b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp index a9d758209a..a9d758209a 100644 --- a/src/platformsupport/dbusmenu/qdbusmenuconnection.cpp +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp diff --git a/src/platformsupport/dbusmenu/qdbusmenuconnection_p.h b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h index ae0595ae3b..ae0595ae3b 100644 --- a/src/platformsupport/dbusmenu/qdbusmenuconnection_p.h +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h diff --git a/src/platformsupport/dbusmenu/qdbusmenuregistrarproxy.cpp b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuregistrarproxy.cpp index c59b5a675e..c59b5a675e 100644 --- a/src/platformsupport/dbusmenu/qdbusmenuregistrarproxy.cpp +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuregistrarproxy.cpp diff --git a/src/platformsupport/dbusmenu/qdbusmenuregistrarproxy_p.h b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuregistrarproxy_p.h index c92de0a140..c92de0a140 100644 --- a/src/platformsupport/dbusmenu/qdbusmenuregistrarproxy_p.h +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuregistrarproxy_p.h diff --git a/src/platformsupport/dbusmenu/qdbusmenutypes.cpp b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenutypes.cpp index 546483fcec..82a13d2fa0 100644 --- a/src/platformsupport/dbusmenu/qdbusmenutypes.cpp +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenutypes.cpp @@ -113,7 +113,8 @@ uint QDBusMenuLayoutItem::populate(int id, int depth, const QStringList &propert void QDBusMenuLayoutItem::populate(const QDBusPlatformMenu *menu, int depth, const QStringList &propertyNames) { - Q_FOREACH (QDBusPlatformMenuItem *item, menu->items()) { + const auto items = menu->items(); + for (QDBusPlatformMenuItem *item : items) { QDBusMenuLayoutItem child; child.populate(item, depth - 1, propertyNames); m_children << child; @@ -136,7 +137,7 @@ const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuLayoutItem &i arg.beginStructure(); arg << item.m_id << item.m_properties; arg.beginArray(qMetaTypeId<QDBusVariant>()); - foreach (const QDBusMenuLayoutItem& child, item.m_children) + for (const QDBusMenuLayoutItem &child : item.m_children) arg << QDBusVariant(QVariant::fromValue<QDBusMenuLayoutItem>(child)); arg.endArray(); arg.endStructure(); @@ -190,11 +191,13 @@ QDBusMenuItem::QDBusMenuItem(const QDBusPlatformMenuItem *item) m_properties.insert(QLatin1String("toggle-type"), toggleType); m_properties.insert(QLatin1String("toggle-state"), item->isChecked() ? 1 : 0); } +#ifndef QT_NO_SHORTCUT const QKeySequence &scut = item->shortcut(); if (!scut.isEmpty()) { QDBusMenuShortcut shortcut = convertKeySequence(scut); m_properties.insert(QLatin1String("shortcut"), QVariant::fromValue(shortcut)); } +#endif const QIcon &icon = item->icon(); if (!icon.name().isEmpty()) { m_properties.insert(QLatin1String("icon-name"), icon.name()); @@ -211,9 +214,9 @@ QDBusMenuItemList QDBusMenuItem::items(const QList<int> &ids, const QStringList { Q_UNUSED(propertyNames) QDBusMenuItemList ret; - QList<const QDBusPlatformMenuItem *> items = QDBusPlatformMenuItem::byIds(ids); + const QList<const QDBusPlatformMenuItem *> items = QDBusPlatformMenuItem::byIds(ids); ret.reserve(items.size()); - Q_FOREACH (const QDBusPlatformMenuItem *item, items) + for (const QDBusPlatformMenuItem *item : items) ret << QDBusMenuItem(item); return ret; } @@ -230,6 +233,7 @@ QString QDBusMenuItem::convertMnemonic(const QString &label) return ret; } +#ifndef QT_NO_SHORTCUT QDBusMenuShortcut QDBusMenuItem::convertKeySequence(const QKeySequence &sequence) { QDBusMenuShortcut shortcut; @@ -258,6 +262,7 @@ QDBusMenuShortcut QDBusMenuItem::convertKeySequence(const QKeySequence &sequence } return shortcut; } +#endif const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuEvent &ev) { diff --git a/src/platformsupport/dbusmenu/qdbusmenutypes_p.h b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenutypes_p.h index 1de71b69e4..fd6727d3be 100644 --- a/src/platformsupport/dbusmenu/qdbusmenutypes_p.h +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenutypes_p.h @@ -74,7 +74,9 @@ public: static QDBusMenuItemList items(const QList<int> &ids, const QStringList &propertyNames); static QString convertMnemonic(const QString &label); +#ifndef QT_NO_SHORTCUT static QDBusMenuShortcut convertKeySequence(const QKeySequence &sequence); +#endif static void registerDBusTypes(); int m_id; diff --git a/src/platformsupport/dbusmenu/qdbusplatformmenu.cpp b/src/platformsupport/themes/genericunix/dbusmenu/qdbusplatformmenu.cpp index 446c3dfa83..15440a03cd 100644 --- a/src/platformsupport/dbusmenu/qdbusplatformmenu.cpp +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusplatformmenu.cpp @@ -134,10 +134,12 @@ void QDBusPlatformMenuItem::setHasExclusiveGroup(bool hasExclusiveGroup) m_hasExclusiveGroup = hasExclusiveGroup; } +#ifndef QT_NO_SHORTCUT void QDBusPlatformMenuItem::setShortcut(const QKeySequence &shortcut) { m_shortcut = shortcut; } +#endif void QDBusPlatformMenuItem::trigger() { @@ -156,7 +158,7 @@ QDBusPlatformMenuItem *QDBusPlatformMenuItem::byId(int id) QList<const QDBusPlatformMenuItem *> QDBusPlatformMenuItem::byIds(const QList<int> &ids) { QList<const QDBusPlatformMenuItem *> ret; - Q_FOREACH (int id, ids) { + for (int id : ids) { if (menuItemsByID.contains(id)) ret << menuItemsByID[id]; } diff --git a/src/platformsupport/dbusmenu/qdbusplatformmenu_p.h b/src/platformsupport/themes/genericunix/dbusmenu/qdbusplatformmenu_p.h index f641ff5d8e..38c27e8051 100644 --- a/src/platformsupport/dbusmenu/qdbusplatformmenu_p.h +++ b/src/platformsupport/themes/genericunix/dbusmenu/qdbusplatformmenu_p.h @@ -99,8 +99,10 @@ public: void setChecked(bool isChecked) Q_DECL_OVERRIDE; bool hasExclusiveGroup() const { return m_hasExclusiveGroup; } void setHasExclusiveGroup(bool hasExclusiveGroup) Q_DECL_OVERRIDE; +#ifndef QT_NO_SHORTCUT QKeySequence shortcut() const { return m_shortcut; } void setShortcut(const QKeySequence& shortcut) Q_DECL_OVERRIDE; +#endif void setIconSize(int size) Q_DECL_OVERRIDE { Q_UNUSED(size); } void setNativeContents(WId item) Q_DECL_OVERRIDE { Q_UNUSED(item); } diff --git a/src/platformsupport/dbustray/dbustray.pri b/src/platformsupport/themes/genericunix/dbustray/dbustray.pri index 734ee5fb40..f703e24ba9 100644 --- a/src/platformsupport/dbustray/dbustray.pri +++ b/src/platformsupport/themes/genericunix/dbustray/dbustray.pri @@ -1,6 +1,6 @@ QT_FOR_PRIVATE += dbus -INCLUDEPATH += $$PWD $$PWD/../../gui/kernel +INCLUDEPATH += $$PWD HEADERS += \ $$PWD/qdbustrayicon_p.h \ diff --git a/src/platformsupport/dbustray/qdbustrayicon.cpp b/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon.cpp index 8ad641c3a7..a686a33464 100644 --- a/src/platformsupport/dbustray/qdbustrayicon.cpp +++ b/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon.cpp @@ -37,22 +37,23 @@ ** ****************************************************************************/ +#include "qdbustrayicon_p.h" + #ifndef QT_NO_SYSTEMTRAYICON -#include "qdbustrayicon_p.h" #include "qdbusmenuconnection_p.h" #include "qstatusnotifieritemadaptor_p.h" #include "qdbusmenuadaptor_p.h" -#include "dbusmenu/qdbusplatformmenu_p.h" +#include "qdbusplatformmenu_p.h" #include "qxdgnotificationproxy_p.h" -#include <qplatformmenu.h> +#include <qpa/qplatformmenu.h> #include <qstring.h> #include <qdebug.h> #include <qrect.h> #include <qloggingcategory.h> -#include <qplatformintegration.h> -#include <qplatformservices.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformservices.h> #include <qdbusconnectioninterface.h> #include <private/qlockfile_p.h> #include <private/qguiapplication_p.h> @@ -303,4 +304,3 @@ bool QDBusTrayIcon::isSystemTrayAvailable() const QT_END_NAMESPACE #endif //QT_NO_SYSTEMTRAYICON - diff --git a/src/platformsupport/dbustray/qdbustrayicon_p.h b/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon_p.h index fa2299866a..234ff60584 100644 --- a/src/platformsupport/dbustray/qdbustrayicon_p.h +++ b/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon_p.h @@ -52,7 +52,9 @@ // We mean it. // -#ifndef QT_NO_SYSTEMTRAYICON +#include <QtGui/private/qtguiglobal_p.h> + +QT_REQUIRE_CONFIG(systemtrayicon); #include <QIcon> #include <QTemporaryFile> @@ -162,6 +164,5 @@ private: }; QT_END_NAMESPACE -#endif // QT_NO_SYSTEMTRAYICON #endif // QDBUSTRAYICON_H diff --git a/src/platformsupport/dbustray/qdbustraytypes.cpp b/src/platformsupport/themes/genericunix/dbustray/qdbustraytypes.cpp index fc0fa00655..fc0fa00655 100644 --- a/src/platformsupport/dbustray/qdbustraytypes.cpp +++ b/src/platformsupport/themes/genericunix/dbustray/qdbustraytypes.cpp diff --git a/src/platformsupport/dbustray/qdbustraytypes_p.h b/src/platformsupport/themes/genericunix/dbustray/qdbustraytypes_p.h index 1bdc855c3c..3f75555579 100644 --- a/src/platformsupport/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/dbustray/qstatusnotifieritemadaptor.cpp b/src/platformsupport/themes/genericunix/dbustray/qstatusnotifieritemadaptor.cpp index 402cdab92c..d3c5454f01 100644 --- a/src/platformsupport/dbustray/qstatusnotifieritemadaptor.cpp +++ b/src/platformsupport/themes/genericunix/dbustray/qstatusnotifieritemadaptor.cpp @@ -48,9 +48,10 @@ QMetaObject::invokeMethod(). */ +#include "qstatusnotifieritemadaptor_p.h" + #ifndef QT_NO_SYSTEMTRAYICON -#include "qstatusnotifieritemadaptor_p.h" #include "qdbustrayicon_p.h" QT_BEGIN_NAMESPACE diff --git a/src/platformsupport/dbustray/qstatusnotifieritemadaptor_p.h b/src/platformsupport/themes/genericunix/dbustray/qstatusnotifieritemadaptor_p.h index deab9e74b5..3f8fca7ac0 100644 --- a/src/platformsupport/dbustray/qstatusnotifieritemadaptor_p.h +++ b/src/platformsupport/themes/genericunix/dbustray/qstatusnotifieritemadaptor_p.h @@ -62,7 +62,9 @@ // We mean it. // -#ifndef QT_NO_SYSTEMTRAYICON +#include <QtGui/private/qtguiglobal_p.h> + +QT_REQUIRE_CONFIG(systemtrayicon); #include <QtCore/QObject> #include <QtDBus/QtDBus> @@ -198,5 +200,5 @@ private: }; QT_END_NAMESPACE -#endif // QT_NO_SYSTEMTRAYICON + #endif // QSTATUSNOTIFIERITEMADAPTER_P_H diff --git a/src/platformsupport/dbustray/qxdgnotificationproxy.cpp b/src/platformsupport/themes/genericunix/dbustray/qxdgnotificationproxy.cpp index ef2aa799c8..ef2aa799c8 100644 --- a/src/platformsupport/dbustray/qxdgnotificationproxy.cpp +++ b/src/platformsupport/themes/genericunix/dbustray/qxdgnotificationproxy.cpp diff --git a/src/platformsupport/dbustray/qxdgnotificationproxy_p.h b/src/platformsupport/themes/genericunix/dbustray/qxdgnotificationproxy_p.h index 03899723ab..03899723ab 100644 --- a/src/platformsupport/dbustray/qxdgnotificationproxy_p.h +++ b/src/platformsupport/themes/genericunix/dbustray/qxdgnotificationproxy_p.h diff --git a/src/platformsupport/themes/genericunix/genericunix.pri b/src/platformsupport/themes/genericunix/genericunix.pri index eed48257d0..27019b4aa2 100644 --- a/src/platformsupport/themes/genericunix/genericunix.pri +++ b/src/platformsupport/themes/genericunix/genericunix.pri @@ -1,2 +1,7 @@ HEADERS += $$PWD/qgenericunixthemes_p.h SOURCES += $$PWD/qgenericunixthemes.cpp + +qtConfig(dbus) { + include(dbusmenu/dbusmenu.pri) + include(dbustray/dbustray.pri) +} diff --git a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp index 9abcd04063..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" @@ -50,6 +49,7 @@ #include <QtCore/QFile> #include <QtCore/QDebug> #include <QtCore/QHash> +#include <QtCore/QMimeDatabase> #include <QtCore/QLoggingCategory> #include <QtCore/QSettings> #include <QtCore/QVariant> @@ -60,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> @@ -231,6 +231,30 @@ QVariant QGenericUnixTheme::themeHint(ThemeHint hint) const return QPlatformTheme::themeHint(hint); } +// Helper functions for implementing QPlatformTheme::fileIcon() for XDG icon themes. +static QList<QSize> availableXdgFileIconSizes() +{ + return QIcon::fromTheme(QStringLiteral("inode-directory")).availableSizes(); +} + +#if QT_CONFIG(mimetype) +static QIcon xdgFileIcon(const QFileInfo &fileInfo) +{ + QMimeDatabase mimeDatabase; + QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo); + if (!mimeType.isValid()) + return QIcon(); + const QString &iconName = mimeType.iconName(); + if (!iconName.isEmpty()) { + const QIcon icon = QIcon::fromTheme(iconName); + if (!icon.isNull()) + return icon; + } + const QString &genericIconName = mimeType.genericIconName(); + return genericIconName.isEmpty() ? QIcon() : QIcon::fromTheme(genericIconName); +} +#endif + #ifndef QT_NO_SETTINGS class QKdeThemePrivate : public QPlatformThemePrivate { @@ -345,7 +369,7 @@ void QKdeThemePrivate::refresh() QVariant QKdeThemePrivate::readKdeSetting(const QString &key, const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings) { - foreach (const QString &kdeDir, kdeDirs) { + for (const QString &kdeDir : kdeDirs) { QSettings *settings = kdeSettings.value(kdeDir); if (!settings) { const QString kdeGlobalsPath = kdeGlobals(kdeDir, kdeVersion); @@ -478,7 +502,7 @@ QStringList QKdeThemePrivate::kdeIconThemeSearchPaths(const QStringList &kdeDirs { QStringList paths = QGenericUnixTheme::xdgIconThemePaths(); const QString iconPath = QStringLiteral("/share/icons"); - foreach (const QString &candidate, kdeDirs) { + for (const QString &candidate : kdeDirs) { const QFileInfo fi(candidate + iconPath); if (fi.isDir()) paths.append(fi.absoluteFilePath()); @@ -506,6 +530,8 @@ QVariant QKdeTheme::themeHint(QPlatformTheme::ThemeHint hint) const return QVariant(d->iconFallbackThemeName); case QPlatformTheme::IconThemeSearchPaths: return QVariant(d->kdeIconThemeSearchPaths(d->kdeDirs)); + case QPlatformTheme::IconPixmapSizes: + return QVariant::fromValue(availableXdgFileIconSizes()); case QPlatformTheme::StyleNames: return QVariant(d->styleNames); case QPlatformTheme::KeyboardScheme: @@ -520,6 +546,15 @@ QVariant QKdeTheme::themeHint(QPlatformTheme::ThemeHint hint) const return QPlatformTheme::themeHint(hint); } +QIcon QKdeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions) const +{ +#if QT_CONFIG(mimetype) + return xdgFileIcon(fileInfo); +#else + return QIcon(); +#endif +} + const QPalette *QKdeTheme::palette(Palette type) const { Q_D(const QKdeTheme); @@ -657,6 +692,8 @@ QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const return QVariant(QStringLiteral("gnome")); case QPlatformTheme::IconThemeSearchPaths: return QVariant(QGenericUnixTheme::xdgIconThemePaths()); + case QPlatformTheme::IconPixmapSizes: + return QVariant::fromValue(availableXdgFileIconSizes()); case QPlatformTheme::StyleNames: { QStringList styleNames; styleNames << QStringLiteral("fusion") << QStringLiteral("windows"); @@ -666,12 +703,23 @@ QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const return QVariant(int(GnomeKeyboardScheme)); case QPlatformTheme::PasswordMaskCharacter: return QVariant(QChar(0x2022)); + case QPlatformTheme::UiEffects: + return QVariant(int(HoverEffect)); default: break; } return QPlatformTheme::themeHint(hint); } +QIcon QGnomeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions) const +{ +#if QT_CONFIG(mimetype) + return xdgFileIcon(fileInfo); +#else + return QIcon(); +#endif +} + const QFont *QGnomeTheme::font(Font type) const { Q_D(const QGnomeTheme); @@ -759,8 +807,8 @@ QStringList QGenericUnixTheme::themeNames() << "MATE" << "XFCE" << "LXDE"; - QList<QByteArray> desktopNames = desktopEnvironment.split(':'); - Q_FOREACH (const QByteArray &desktopName, desktopNames) { + const QList<QByteArray> desktopNames = desktopEnvironment.split(':'); + for (const QByteArray &desktopName : desktopNames) { if (desktopEnvironment == "KDE") { #ifndef QT_NO_SETTINGS result.push_back(QLatin1String(QKdeTheme::name)); diff --git a/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h b/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h index 952658e130..da13390662 100644 --- a/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h +++ b/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h @@ -107,6 +107,9 @@ public: static QPlatformTheme *createKdeTheme(); QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE; + QIcon fileIcon(const QFileInfo &fileInfo, + QPlatformTheme::IconOptions iconOptions = 0) const override; + const QPalette *palette(Palette type = SystemPalette) const Q_DECL_OVERRIDE; const QFont *font(Font type) const Q_DECL_OVERRIDE; @@ -129,6 +132,8 @@ class QGnomeTheme : public QPlatformTheme public: QGnomeTheme(); QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE; + QIcon fileIcon(const QFileInfo &fileInfo, + QPlatformTheme::IconOptions = 0) const override; const QFont *font(Font type) const Q_DECL_OVERRIDE; QString standardButtonText(int button) const Q_DECL_OVERRIDE; diff --git a/src/platformsupport/themes/qabstractfileiconengine.cpp b/src/platformsupport/themes/qabstractfileiconengine.cpp new file mode 100644 index 0000000000..192ed00510 --- /dev/null +++ b/src/platformsupport/themes/qabstractfileiconengine.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** 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 "qabstractfileiconengine_p.h" + +#include <qpixmapcache.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractFileIconEngine + \brief Helper base class for retrieving icons for files for usage by QFileIconProvider and related. + + Reimplement availableSizes() and new virtual filePixmap() and return icons created + with this engine from QPlatformTheme::fileIcon(). + + Note: The class internally caches pixmaps for files by suffix (with the exception + of some files on Windows), but not for directories (since directory icons may have + overlay icons on Windows). You might want to cache pixmaps for directories + in your implementation. + + \since 5.8 + \internal + \sa QFileIconProvider::DontUseCustomDirectoryIcons, QPlatformTheme + \ingroup qpa +*/ +QPixmap QAbstractFileIconEngine::pixmap(const QSize &size, QIcon::Mode mode, + QIcon::State state) +{ + Q_UNUSED(mode); + Q_UNUSED(state); + + if (!size.isValid()) + return QPixmap(); + + QString key = cacheKey(); + if (key.isEmpty()) + return filePixmap(size, mode, state); + + key += QLatin1Char('_') + QString::number(size.width()); + + QPixmap result; + if (!QPixmapCache::find(key, result)) { + result = filePixmap(size, mode, state); + if (!result.isNull()) + QPixmapCache::insert(key, result); + } + + return result; +} + +QSize QAbstractFileIconEngine::actualSize(const QSize &size, QIcon::Mode mode, + QIcon::State state) +{ + const QList<QSize> &sizes = availableSizes(mode, state); + const int numberSizes = sizes.length(); + if (numberSizes == 0) + return QSize(); + + // Find the smallest available size whose area is still larger than the input + // size. Otherwise, use the largest area available size. (We don't assume the + // platform theme sizes are sorted, hence the extra logic.) + const int sizeArea = size.width() * size.height(); + QSize actualSize = sizes.first(); + int actualArea = actualSize.width() * actualSize.height(); + for (int i = 1; i < numberSizes; ++i) { + const QSize &s = sizes.at(i); + const int a = s.width() * s.height(); + if ((sizeArea <= a && a < actualArea) || (actualArea < sizeArea && actualArea < a)) { + actualSize = s; + actualArea = a; + } + } + + if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height())) + actualSize.scale(size, Qt::KeepAspectRatio); + + return actualSize; +} + +/* Reimplement to return a cache key for the entry. An empty result indicates + * the icon should not be cached (for example, directory icons having custom icons). */ +QString QAbstractFileIconEngine::cacheKey() const +{ + if (!m_fileInfo.isFile() || m_fileInfo.isSymLink() || m_fileInfo.isExecutable()) + return QString(); + + const QString &suffix = m_fileInfo.suffix(); + return QLatin1String("qt_.") + + (suffix.isEmpty() ? m_fileInfo.fileName() : suffix); // handle "Makefile" ;) +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/themes/qabstractfileiconengine_p.h b/src/platformsupport/themes/qabstractfileiconengine_p.h new file mode 100644 index 0000000000..ce38cf262e --- /dev/null +++ b/src/platformsupport/themes/qabstractfileiconengine_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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 QABSTRACTFILEICONENGINE_P_H +#define QABSTRACTFILEICONENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qfileinfo.h> +#include <private/qicon_p.h> +#include <qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +class QAbstractFileIconEngine : public QPixmapIconEngine +{ +public: + explicit QAbstractFileIconEngine(const QFileInfo &info, QPlatformTheme::IconOptions opts) + : QPixmapIconEngine(), m_fileInfo(info), m_options(opts) {} + + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State) override; + QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override; + + QFileInfo fileInfo() const { return m_fileInfo; } + QPlatformTheme::IconOptions options() const { return m_options; } + + // Helper to convert a sequence of ints to a list of QSize + template <class It> static QList<QSize> toSizeList(It i1, It i2); + +protected: + virtual QPixmap filePixmap(const QSize &size, QIcon::Mode mode, QIcon::State) = 0; + virtual QString cacheKey() const; + +private: + const QFileInfo m_fileInfo; + const QPlatformTheme::IconOptions m_options; +}; + +template <class It> +inline QList<QSize> QAbstractFileIconEngine::toSizeList(It i1, It i2) +{ + QList<QSize> result; + result.reserve(int(i2 - i1)); + for ( ; i1 != i2; ++i1) + result.append(QSize(*i1, *i1)); + return result; +} + +QT_END_NAMESPACE + +#endif // QABSTRACTFILEICONENGINE_P_H diff --git a/src/platformsupport/themes/themes.pri b/src/platformsupport/themes/themes.pri deleted file mode 100644 index adee852626..0000000000 --- a/src/platformsupport/themes/themes.pri +++ /dev/null @@ -1,3 +0,0 @@ -unix:!mac { - include($$PWD/genericunix/genericunix.pri) -} 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) |