From 7f179eff61826323afea474b8ecd1a04f0a41e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 11 May 2020 17:57:24 +0200 Subject: Move macOS print support from platform plugin into QtPrintSupport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-83256 Change-Id: I29044b6c3f952c259f501f94a175c8ef2cbaae55 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/CMakeLists.txt | 18 - src/plugins/platforms/cocoa/cocoa.pro | 17 +- .../platforms/cocoa/qcocoanativeinterface.h | 20 - .../platforms/cocoa/qcocoanativeinterface.mm | 30 - src/plugins/platforms/cocoa/qcocoaprintdevice.h | 122 -- src/plugins/platforms/cocoa/qcocoaprintdevice.mm | 498 ------- src/plugins/platforms/cocoa/qcocoaprintersupport.h | 65 - .../platforms/cocoa/qcocoaprintersupport.mm | 111 -- src/plugins/platforms/cocoa/qpaintengine_mac.mm | 1428 -------------------- src/plugins/platforms/cocoa/qpaintengine_mac_p.h | 205 --- src/plugins/platforms/cocoa/qprintengine_mac.mm | 797 ----------- src/plugins/platforms/cocoa/qprintengine_mac_p.h | 157 --- src/plugins/printsupport/cocoa/main.cpp | 16 +- src/printsupport/CMakeLists.txt | 11 + src/printsupport/dialogs/qpagesetupdialog_mac.mm | 10 +- src/printsupport/dialogs/qprintdialog_mac.mm | 10 +- src/printsupport/platform/macos/macos.pri | 13 + .../platform/macos/qcocoaprintdevice.mm | 498 +++++++ .../platform/macos/qcocoaprintdevice_p.h | 126 ++ .../platform/macos/qcocoaprintersupport.mm | 113 ++ .../platform/macos/qcocoaprintersupport_p.h | 78 ++ .../platform/macos/qpaintengine_mac.mm | 1422 +++++++++++++++++++ .../platform/macos/qpaintengine_mac_p.h | 207 +++ .../platform/macos/qprintengine_mac.mm | 805 +++++++++++ .../platform/macos/qprintengine_mac_p.h | 159 +++ src/printsupport/printsupport.pro | 2 + 26 files changed, 3445 insertions(+), 3493 deletions(-) delete mode 100644 src/plugins/platforms/cocoa/qcocoaprintdevice.h delete mode 100644 src/plugins/platforms/cocoa/qcocoaprintdevice.mm delete mode 100644 src/plugins/platforms/cocoa/qcocoaprintersupport.h delete mode 100644 src/plugins/platforms/cocoa/qcocoaprintersupport.mm delete mode 100644 src/plugins/platforms/cocoa/qpaintengine_mac.mm delete mode 100644 src/plugins/platforms/cocoa/qpaintengine_mac_p.h delete mode 100644 src/plugins/platforms/cocoa/qprintengine_mac.mm delete mode 100644 src/plugins/platforms/cocoa/qprintengine_mac_p.h create mode 100644 src/printsupport/platform/macos/macos.pri create mode 100644 src/printsupport/platform/macos/qcocoaprintdevice.mm create mode 100644 src/printsupport/platform/macos/qcocoaprintdevice_p.h create mode 100644 src/printsupport/platform/macos/qcocoaprintersupport.mm create mode 100644 src/printsupport/platform/macos/qcocoaprintersupport_p.h create mode 100644 src/printsupport/platform/macos/qpaintengine_mac.mm create mode 100644 src/printsupport/platform/macos/qpaintengine_mac_p.h create mode 100644 src/printsupport/platform/macos/qprintengine_mac.mm create mode 100644 src/printsupport/platform/macos/qprintengine_mac_p.h diff --git a/src/plugins/platforms/cocoa/CMakeLists.txt b/src/plugins/platforms/cocoa/CMakeLists.txt index 9d578e7eed..11f3f99d8d 100644 --- a/src/plugins/platforms/cocoa/CMakeLists.txt +++ b/src/plugins/platforms/cocoa/CMakeLists.txt @@ -1,7 +1,6 @@ # Generated from cocoa.pro. # special case: -qt_find_package(Cups PROVIDED_TARGETS Cups::Cups) ##################################################################### ## QCocoaIntegrationPlugin Plugin: @@ -55,7 +54,6 @@ qt_internal_add_plugin(QCocoaIntegrationPlugin ${FWIOSurface} ${FWMetal} ${FWQuartzCore} - Cups::Cups Qt::Core Qt::CorePrivate Qt::Gui @@ -110,25 +108,9 @@ qt_extend_target(QCocoaIntegrationPlugin CONDITION QT_FEATURE_sessionmanager qcocoasessionmanager.cpp qcocoasessionmanager.h ) -qt_extend_target(QCocoaIntegrationPlugin CONDITION TARGET Qt::Widgets - SOURCES - qpaintengine_mac.mm qpaintengine_mac_p.h - PUBLIC_LIBRARIES - Qt::WidgetsPrivate -) - #### Keys ignored in scope 7:.:.:cocoa.pro:TARGET Qt::Widgets: # QT_FOR_CONFIG = "widgets" -qt_extend_target(QCocoaIntegrationPlugin CONDITION TARGET Qt::PrintSupport AND TARGET Qt::Widgets - SOURCES - qcocoaprintdevice.h qcocoaprintdevice.mm - qcocoaprintersupport.h qcocoaprintersupport.mm - qprintengine_mac.mm qprintengine_mac_p.h - PUBLIC_LIBRARIES - Qt::PrintSupportPrivate -) - qt_extend_target(QCocoaIntegrationPlugin CONDITION QT_FEATURE_colordialog AND TARGET Qt::Widgets SOURCES qcocoacolordialoghelper.h qcocoacolordialoghelper.mm diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index c0a7b4e6cf..a33b99e898 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -89,7 +89,7 @@ qtConfig(sessionmanager) { RESOURCES += qcocoaresources.qrc -LIBS += -framework AppKit -framework CoreServices -framework Carbon -framework IOKit -framework QuartzCore -framework CoreVideo -framework Metal -framework IOSurface -lcups +LIBS += -framework AppKit -framework CoreServices -framework Carbon -framework IOKit -framework QuartzCore -framework CoreVideo -framework Metal -framework IOSurface DEFINES += QT_NO_FOREACH @@ -100,21 +100,6 @@ CONFIG += no_app_extension_api_only qtHaveModule(widgets) { QT_FOR_CONFIG += widgets - SOURCES += qpaintengine_mac.mm - HEADERS += qpaintengine_mac_p.h - - qtHaveModule(printsupport) { - QT += printsupport-private - SOURCES += \ - qprintengine_mac.mm \ - qcocoaprintersupport.mm \ - qcocoaprintdevice.mm - HEADERS += \ - qcocoaprintersupport.h \ - qcocoaprintdevice.h \ - qprintengine_mac_p.h - } - qtConfig(colordialog) { SOURCES += qcocoacolordialoghelper.mm HEADERS += qcocoacolordialoghelper.h diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h index c30297c8ca..45330cf1b8 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h @@ -45,13 +45,10 @@ #include #include Q_MOC_INCLUDE() -Q_MOC_INCLUDE() -Q_MOC_INCLUDE() QT_BEGIN_NAMESPACE class QWidget; -class QPlatformPrinterSupport; class QPrintEngine; class QPlatformMenu; class QPlatformMenuBar; @@ -80,23 +77,6 @@ public Q_SLOTS: void onAppFocusWindowChanged(QWindow *window); private: - /* - "Virtual" function to create the platform printer support - implementation. - - We use an invokable function instead of a virtual one, we do not want - this in the QPlatform* API yet. - - This was added here only because QPlatformNativeInterface is a QObject - and allow us to use QMetaObject::indexOfMethod() from the printsupport - plugin. - */ - Q_INVOKABLE QPlatformPrinterSupport *createPlatformPrinterSupport(); - /* - Function to return the NSPrintInfo * from QMacPaintEnginePrivate. - Needed by the native print dialog in the Qt Print Support module. - */ - Q_INVOKABLE void *NSPrintInfoForPrintEngine(QPrintEngine *printEngine); /* Function to return the default background pixmap. Needed by QWizard in the Qt widget module. diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm index 8e336d9ee7..fbc2e8f13e 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm @@ -59,12 +59,6 @@ #include #include -#if !defined(QT_NO_WIDGETS) && defined(QT_PRINTSUPPORT_LIB) -#include "qcocoaprintersupport.h" -#include "qprintengine_mac_p.h" -#include -#endif - #include #include @@ -147,30 +141,6 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QCocoaNativeInter return nullptr; } -QPlatformPrinterSupport *QCocoaNativeInterface::createPlatformPrinterSupport() -{ -#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) && defined(QT_PRINTSUPPORT_LIB) - return new QCocoaPrinterSupport(); -#else - qFatal("Printing is not supported when Qt is configured with -no-widgets or -no-feature-printer"); - return nullptr; -#endif -} - -void *QCocoaNativeInterface::NSPrintInfoForPrintEngine(QPrintEngine *printEngine) -{ -#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) && defined(QT_PRINTSUPPORT_LIB) - QMacPrintEnginePrivate *macPrintEnginePriv = static_cast(printEngine)->d_func(); - if (macPrintEnginePriv->state == QPrinter::Idle && !macPrintEnginePriv->isPrintSessionInitialized()) - macPrintEnginePriv->initialize(); - return macPrintEnginePriv->printInfo; -#else - Q_UNUSED(printEngine); - qFatal("Printing is not supported when Qt is configured with -no-widgets or -no-feature-printer"); - return nullptr; -#endif -} - QPixmap QCocoaNativeInterface::defaultBackgroundPixmapForQWizard() { // Note: starting with macOS 10.14, the KeyboardSetupAssistant app bundle no diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.h b/src/plugins/platforms/cocoa/qcocoaprintdevice.h deleted file mode 100644 index 59a521e0b5..0000000000 --- a/src/plugins/platforms/cocoa/qcocoaprintdevice.h +++ /dev/null @@ -1,122 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 John Layt -** 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 QCOCOAPRINTDEVICE_H -#define QCOCOAPRINTDEVICE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of internal files. This header file may change from version to version -// without notice, or even be removed. -// -// We mean it. -// - -#include - -#ifndef QT_NO_PRINTER - -#include - -QT_BEGIN_NAMESPACE - -class QCocoaPrintDevice : public QPlatformPrintDevice -{ -public: - QCocoaPrintDevice(); - explicit QCocoaPrintDevice(const QString &id); - virtual ~QCocoaPrintDevice(); - - bool isValid() const override; - bool isDefault() const override; - - QPrint::DeviceState state() const override; - - QPageSize defaultPageSize() const override; - - QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation, - int resolution) const override; - - int defaultResolution() const override; - - QPrint::InputSlot defaultInputSlot() const override; - - QPrint::OutputBin defaultOutputBin() const override; - - QPrint::DuplexMode defaultDuplexMode() const override; - - QPrint::ColorMode defaultColorMode() const override; - - PMPrinter macPrinter() const; - PMPaper macPaper(const QPageSize &pageSize) const; - -protected: - void loadPageSizes() const override; - void loadResolutions() const override; - void loadInputSlots() const override; - void loadOutputBins() const override; - void loadDuplexModes() const override; - void loadColorModes() const override; -#if QT_CONFIG(mimetype) - void loadMimeTypes() const override; -#endif - -private: - QPageSize createPageSize(const PMPaper &paper) const; - bool openPpdFile(); - - // Mac Core Printing - PMPrinter m_printer; - PMPrintSession m_session; - mutable QHash m_macPapers; - - // PPD File - ppd_file_t *m_ppd; - - QMarginsF m_customMargins; - mutable QHash m_printableMargins; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_PRINTER -#endif // QCOCOAPRINTDEVICE_H diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm deleted file mode 100644 index ab304d9c04..0000000000 --- a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm +++ /dev/null @@ -1,498 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 John Layt -** 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 - -#include "qcocoaprintdevice.h" - -#if QT_CONFIG(mimetype) -#include -#endif -#include - -#include - -QT_BEGIN_NAMESPACE - -#ifndef QT_NO_PRINTER - -// The CUPS PPD APIs were deprecated in CUPS 1.6/macOS 10.8, but -// as long as we're supporting RHEL 6, which still ships CUPS 1.4 -// we're not going to rewrite this, as we want to share the code -// between macOS and Linux for the CUPS-bits. See discussion in -// https://bugreports.qt.io/browse/QTBUG-56545 -#pragma message "Disabling CUPS PPD deprecation warnings. This should be fixed once we drop support for RHEL6 (QTBUG-56545)" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - -static QPrint::DuplexMode macToDuplexMode(const PMDuplexMode &mode) -{ - if (mode == kPMDuplexTumble) - return QPrint::DuplexShortSide; - else if (mode == kPMDuplexNoTumble) - return QPrint::DuplexLongSide; - else // kPMDuplexNone or kPMSimplexTumble - return QPrint::DuplexNone; -} - -QCocoaPrintDevice::QCocoaPrintDevice() - : QPlatformPrintDevice(), - m_printer(nullptr), - m_session(nullptr), - m_ppd(nullptr) -{ -} - -QCocoaPrintDevice::QCocoaPrintDevice(const QString &id) - : QPlatformPrintDevice(id), - m_printer(nullptr), - m_session(nullptr), - m_ppd(nullptr) -{ - if (!id.isEmpty()) { - m_printer = PMPrinterCreateFromPrinterID(id.toCFString()); - if (m_printer) { - m_name = QString::fromCFString(PMPrinterGetName(m_printer)); - m_location = QString::fromCFString(PMPrinterGetLocation(m_printer)); - CFStringRef cfMakeAndModel; - if (PMPrinterGetMakeAndModelName(m_printer, &cfMakeAndModel) == noErr) - m_makeAndModel = QString::fromCFString(cfMakeAndModel); - Boolean isRemote; - if (PMPrinterIsRemote(m_printer, &isRemote) == noErr) - m_isRemote = isRemote; - if (PMCreateSession(&m_session) == noErr) - PMSessionSetCurrentPMPrinter(m_session, m_printer); - - // No native api to query these options, need to use PPD directly, note is deprecated from 1.6 onwards - if (openPpdFile()) { - // Note this is if the hardware does multiple copies, not if Cups can - m_supportsMultipleCopies = !m_ppd->manual_copies; - // Note this is if the hardware does collation, not if Cups can - ppd_option_t *collate = ppdFindOption(m_ppd, "Collate"); - if (collate) - m_supportsCollateCopies = true; - m_supportsCustomPageSizes = m_ppd->custom_max[0] > 0 && m_ppd->custom_max[1] > 0; - m_minimumPhysicalPageSize = QSize(m_ppd->custom_min[0], m_ppd->custom_min[1]); - m_maximumPhysicalPageSize = QSize(m_ppd->custom_max[0], m_ppd->custom_max[1]); - m_customMargins = QMarginsF(m_ppd->custom_margins[0], m_ppd->custom_margins[3], - m_ppd->custom_margins[2], m_ppd->custom_margins[1]); - } - } - } -} - -QCocoaPrintDevice::~QCocoaPrintDevice() -{ - if (m_ppd) - ppdClose(m_ppd); - for (PMPaper paper : m_macPapers) - PMRelease(paper); - // Releasing the session appears to also release the printer - if (m_session) - PMRelease(m_session); - else if (m_printer) - PMRelease(m_printer); -} - -bool QCocoaPrintDevice::isValid() const -{ - return m_printer ? true : false; -} - -bool QCocoaPrintDevice::isDefault() const -{ - return PMPrinterIsDefault(m_printer); -} - -QPrint::DeviceState QCocoaPrintDevice::state() const -{ - PMPrinterState state; - if (PMPrinterGetState(m_printer, &state) == noErr) { - if (state == kPMPrinterIdle) - return QPrint::Idle; - else if (state == kPMPrinterProcessing) - return QPrint::Active; - else if (state == kPMPrinterStopped) - return QPrint::Error; - } - return QPrint::Error; -} - -QPageSize QCocoaPrintDevice::createPageSize(const PMPaper &paper) const -{ - CFStringRef key; - double width; - double height; - CFStringRef localizedName; - if (PMPaperGetPPDPaperName(paper, &key) == noErr - && PMPaperGetWidth(paper, &width) == noErr - && PMPaperGetHeight(paper, &height) == noErr - && PMPaperCreateLocalizedName(paper, m_printer, &localizedName) == noErr) { - QPageSize pageSize = QPlatformPrintDevice::createPageSize(QString::fromCFString(key),QSize(width, height), - QString::fromCFString(localizedName)); - CFRelease(localizedName); - return pageSize; - } - return QPageSize(); -} - -void QCocoaPrintDevice::loadPageSizes() const -{ - m_pageSizes.clear(); - for (PMPaper paper : m_macPapers) - PMRelease(paper); - m_macPapers.clear(); - m_printableMargins.clear(); - CFArrayRef paperSizes; - if (PMPrinterGetPaperList(m_printer, &paperSizes) == noErr) { - int count = CFArrayGetCount(paperSizes); - for (int i = 0; i < count; ++i) { - PMPaper paper = static_cast(const_cast(CFArrayGetValueAtIndex(paperSizes, i))); - QPageSize pageSize = createPageSize(paper); - if (pageSize.isValid()) { - m_pageSizes.append(pageSize); - PMRetain(paper); - m_macPapers.insert(pageSize.key(), paper); - PMPaperMargins printMargins; - PMPaperGetMargins(paper, &printMargins); - m_printableMargins.insert(pageSize.key(), QMarginsF(printMargins.left, printMargins.top, - printMargins.right, printMargins.bottom)); - } - } - } - m_havePageSizes = true; -} - -QPageSize QCocoaPrintDevice::defaultPageSize() const -{ - QPageSize pageSize; - PMPageFormat pageFormat; - PMPaper paper; - if (PMCreatePageFormat(&pageFormat) == noErr) { - if (PMSessionDefaultPageFormat(m_session, pageFormat) == noErr - && PMGetPageFormatPaper(pageFormat, &paper) == noErr) { - pageSize = createPageSize(paper); - } - PMRelease(pageFormat); - } - return pageSize; -} - -QMarginsF QCocoaPrintDevice::printableMargins(const QPageSize &pageSize, - QPageLayout::Orientation orientation, - int resolution) const -{ - Q_UNUSED(orientation) - Q_UNUSED(resolution) - if (!m_havePageSizes) - loadPageSizes(); - if (m_printableMargins.contains(pageSize.key())) - return m_printableMargins.value(pageSize.key()); - return m_customMargins; -} - -void QCocoaPrintDevice::loadResolutions() const -{ - m_resolutions.clear(); - UInt32 count; - if (PMPrinterGetPrinterResolutionCount(m_printer, &count) == noErr) { - // 1-based index - for (UInt32 i = 1; i <= count; ++i) { - PMResolution resolution; - if (PMPrinterGetIndexedPrinterResolution(m_printer, i, &resolution) == noErr) - m_resolutions.append(int(resolution.hRes)); - } - } - m_haveResolutions = true; -} - -int QCocoaPrintDevice::defaultResolution() const -{ - int defaultResolution = 72; - PMPrintSettings settings; - if (PMCreatePrintSettings(&settings) == noErr) { - PMResolution resolution; - if (PMSessionDefaultPrintSettings(m_session, settings) == noErr - && PMPrinterGetOutputResolution(m_printer, settings, &resolution) == noErr) { - // PMPrinterGetOutputResolution usually fails with -9589 kPMKeyNotFound as not set in PPD - defaultResolution = int(resolution.hRes); - } - PMRelease(settings); - } - // If no value returned (usually means not set in PPD) then use supported resolutions which - // OSX will have populated with at least one default value (but why not returned by call?) - if (defaultResolution <= 0) { - if (!m_haveResolutions) - loadResolutions(); - if (m_resolutions.count() > 0) - return m_resolutions.at(0); // First value or highest? Only likely to be one anyway. - return 72; // TDOD More sensible default value??? - } - return defaultResolution; -} - -void QCocoaPrintDevice::loadInputSlots() const -{ - // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync - // TODO Deal with concatenated names like Tray1Manual or Tray1_Man, - // will currently show as CustomInputSlot - // TODO Deal with separate ManualFeed key - // Try load standard PPD options first - m_inputSlots.clear(); - if (m_ppd) { - ppd_option_t *inputSlots = ppdFindOption(m_ppd, "InputSlot"); - if (inputSlots) { - for (int i = 0; i < inputSlots->num_choices; ++i) - m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[i])); - } - // If no result, try just the default - if (m_inputSlots.size() == 0) { - inputSlots = ppdFindOption(m_ppd, "DefaultInputSlot"); - if (inputSlots) - m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[0])); - } - } - // If still no result, just use Auto - if (m_inputSlots.size() == 0) - m_inputSlots.append(QPlatformPrintDevice::defaultInputSlot()); - m_haveInputSlots = true; -} - -QPrint::InputSlot QCocoaPrintDevice::defaultInputSlot() const -{ - // No native api to query, use PPD directly - // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync - // Try load standard PPD option first - if (m_ppd) { - ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultInputSlot"); - if (inputSlot) - return QPrintUtils::ppdChoiceToInputSlot(inputSlot->choices[0]); - // If no result, then try a marked option - ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "InputSlot"); - if (defaultChoice) - return QPrintUtils::ppdChoiceToInputSlot(*defaultChoice); - } - // Otherwise return Auto - return QPlatformPrintDevice::defaultInputSlot(); -} - -void QCocoaPrintDevice::loadOutputBins() const -{ - // No native api to query, use PPD directly - // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync - m_outputBins.clear(); - if (m_ppd) { - ppd_option_t *outputBins = ppdFindOption(m_ppd, "OutputBin"); - if (outputBins) { - for (int i = 0; i < outputBins->num_choices; ++i) - m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[i])); - } - // If no result, try just the default - if (m_outputBins.size() == 0) { - outputBins = ppdFindOption(m_ppd, "DefaultOutputBin"); - if (outputBins) - m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[0])); - } - } - // If still no result, just use Auto - if (m_outputBins.size() == 0) - m_outputBins.append(QPlatformPrintDevice::defaultOutputBin()); - m_haveOutputBins = true; -} - -QPrint::OutputBin QCocoaPrintDevice::defaultOutputBin() const -{ - // No native api to query, use PPD directly - // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync - // Try load standard PPD option first - if (m_ppd) { - ppd_option_t *outputBin = ppdFindOption(m_ppd, "DefaultOutputBin"); - if (outputBin) - return QPrintUtils::ppdChoiceToOutputBin(outputBin->choices[0]); - // If no result, then try a marked option - ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "OutputBin"); - if (defaultChoice) - return QPrintUtils::ppdChoiceToOutputBin(*defaultChoice); - } - // Otherwise return AutoBin - return QPlatformPrintDevice::defaultOutputBin(); -} - -void QCocoaPrintDevice::loadDuplexModes() const -{ - // No native api to query, use PPD directly - // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync - // Try load standard PPD options first - m_duplexModes.clear(); - if (m_ppd) { - ppd_option_t *duplexModes = ppdFindOption(m_ppd, "Duplex"); - if (duplexModes) { - for (int i = 0; i < duplexModes->num_choices; ++i) - m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[i].choice)); - } - // If no result, try just the default - if (m_duplexModes.size() == 0) { - duplexModes = ppdFindOption(m_ppd, "DefaultDuplex"); - if (duplexModes) - m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[0].choice)); - } - } - // If still no result, or not added in PPD, then add None - if (m_duplexModes.size() == 0 || !m_duplexModes.contains(QPrint::DuplexNone)) - m_duplexModes.append(QPrint::DuplexNone); - // If have both modes, then can support DuplexAuto - if (m_duplexModes.contains(QPrint::DuplexLongSide) && m_duplexModes.contains(QPrint::DuplexShortSide)) - m_duplexModes.append(QPrint::DuplexAuto); - m_haveDuplexModes = true; -} - -QPrint::DuplexMode QCocoaPrintDevice::defaultDuplexMode() const -{ - QPrint::DuplexMode defaultMode = QPrint::DuplexNone; - PMPrintSettings settings; - if (PMCreatePrintSettings(&settings) == noErr) { - PMDuplexMode duplexMode; - if (PMSessionDefaultPrintSettings(m_session, settings) == noErr - && PMGetDuplex(settings, &duplexMode) == noErr) { - defaultMode = macToDuplexMode(duplexMode); - } - PMRelease(settings); - } - return defaultMode; -} - -void QCocoaPrintDevice::loadColorModes() const -{ - // No native api to query, use PPD directly - m_colorModes.clear(); - m_colorModes.append(QPrint::GrayScale); - if (!m_ppd || (m_ppd && m_ppd->color_device)) - m_colorModes.append(QPrint::Color); - m_haveColorModes = true; -} - -QPrint::ColorMode QCocoaPrintDevice::defaultColorMode() const -{ - // No native api to query, use PPD directly - // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync - // Not a proper option, usually only know if supports color or not, but some - // users known to abuse ColorModel to always force GrayScale. - if (m_ppd && supportedColorModes().contains(QPrint::Color)) { - ppd_option_t *colorModel = ppdFindOption(m_ppd, "DefaultColorModel"); - if (!colorModel) - colorModel = ppdFindOption(m_ppd, "ColorModel"); - if (!colorModel || qstrcmp(colorModel->defchoice, "Gray") != 0) - return QPrint::Color; - } - return QPrint::GrayScale; -} - -#if QT_CONFIG(mimetype) -void QCocoaPrintDevice::loadMimeTypes() const -{ - // TODO Check how settings affect returned list - m_mimeTypes.clear(); - QMimeDatabase db; - PMPrintSettings settings; - if (PMCreatePrintSettings(&settings) == noErr) { - CFArrayRef mimeTypes; - if (PMPrinterGetMimeTypes(m_printer, settings, &mimeTypes) == noErr) { - int count = CFArrayGetCount(mimeTypes); - for (int i = 0; i < count; ++i) { - CFStringRef mimeName = static_cast(const_cast(CFArrayGetValueAtIndex(mimeTypes, i))); - QMimeType mimeType = db.mimeTypeForName(QString::fromCFString(mimeName)); - if (mimeType.isValid()) - m_mimeTypes.append(mimeType); - } - } - PMRelease(settings); - } - m_haveMimeTypes = true; -} -#endif // mimetype - -bool QCocoaPrintDevice::openPpdFile() -{ - if (m_ppd) - ppdClose(m_ppd); - m_ppd = nullptr; - CFURLRef ppdURL = nullptr; - char ppdPath[MAXPATHLEN]; - if (PMPrinterCopyDescriptionURL(m_printer, kPMPPDDescriptionType, &ppdURL) == noErr - && ppdURL) { - if (CFURLGetFileSystemRepresentation(ppdURL, true, (UInt8*)ppdPath, sizeof(ppdPath))) - m_ppd = ppdOpenFile(ppdPath); - CFRelease(ppdURL); - } - return m_ppd ? true : false; -} - -PMPrinter QCocoaPrintDevice::macPrinter() const -{ - return m_printer; -} - -// Returns a cached printer PMPaper, or creates and caches a new custom PMPaper -// Caller should never release a cached PMPaper! -PMPaper QCocoaPrintDevice::macPaper(const QPageSize &pageSize) const -{ - if (!m_havePageSizes) - loadPageSizes(); - // If keys match, then is a supported size or an existing custom size - if (m_macPapers.contains(pageSize.key())) - return m_macPapers.value(pageSize.key()); - // For any other page size, whether custom or just unsupported, needs to be a custom PMPaper - PMPaper paper = nullptr; - PMPaperMargins paperMargins; - paperMargins.left = m_customMargins.left(); - paperMargins.right = m_customMargins.right(); - paperMargins.top = m_customMargins.top(); - paperMargins.bottom = m_customMargins.bottom(); - PMPaperCreateCustom(m_printer, QCFString(pageSize.key()), QCFString(pageSize.name()), - pageSize.sizePoints().width(), pageSize.sizePoints().height(), - &paperMargins, &paper); - m_macPapers.insert(pageSize.key(), paper); - return paper; -} - -#pragma clang diagnostic pop - -#endif // QT_NO_PRINTER - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaprintersupport.h b/src/plugins/platforms/cocoa/qcocoaprintersupport.h deleted file mode 100644 index b1a9541c03..0000000000 --- a/src/plugins/platforms/cocoa/qcocoaprintersupport.h +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtPrintSupport 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 QCOCOAPRINTERSUPPORT_H -#define QCOCOAPRINTERSUPPORT_H - -#include -#ifndef QT_NO_PRINTER - -QT_BEGIN_NAMESPACE - -class QCocoaPrinterSupport : public QPlatformPrinterSupport -{ -public: - QCocoaPrinterSupport(); - ~QCocoaPrinterSupport(); - - QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) override; - QPaintEngine *createPaintEngine(QPrintEngine *, QPrinter::PrinterMode printerMode) override; - - QPrintDevice createPrintDevice(const QString &id) override; - QStringList availablePrintDeviceIds() const override; - QString defaultPrintDeviceId() const override; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_PRINTER -#endif // QCOCOAPRINTERSUPPORT_H diff --git a/src/plugins/platforms/cocoa/qcocoaprintersupport.mm b/src/plugins/platforms/cocoa/qcocoaprintersupport.mm deleted file mode 100644 index 4c5c7aef88..0000000000 --- a/src/plugins/platforms/cocoa/qcocoaprintersupport.mm +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtPrintSupport 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 "qcocoaprintersupport.h" - -#ifndef QT_NO_PRINTER - -#include - -#include - -#include "qcocoaprintdevice.h" -#include "qprintengine_mac_p.h" - -#include - -QT_BEGIN_NAMESPACE - -QCocoaPrinterSupport::QCocoaPrinterSupport() -{ } - -QCocoaPrinterSupport::~QCocoaPrinterSupport() -{ } - -QPrintEngine *QCocoaPrinterSupport::createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId) -{ - return new QMacPrintEngine(printerMode, deviceId); -} - -QPaintEngine *QCocoaPrinterSupport::createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode printerMode) -{ - Q_UNUSED(printerMode); - /* - QMacPrintEngine multiply inherits from QPrintEngine and QPaintEngine, - the cast here allows conversion of QMacPrintEngine* to QPaintEngine* - */ - return static_cast(printEngine); -} - -QPrintDevice QCocoaPrinterSupport::createPrintDevice(const QString &id) -{ - return QPlatformPrinterSupport::createPrintDevice(new QCocoaPrintDevice(id)); -} - -QStringList QCocoaPrinterSupport::availablePrintDeviceIds() const -{ - QStringList list; - QCFType printerList; - if (PMServerCreatePrinterList(kPMServerLocal, &printerList) == noErr) { - CFIndex count = CFArrayGetCount(printerList); - for (CFIndex i = 0; i < count; ++i) { - PMPrinter printer = static_cast(const_cast(CFArrayGetValueAtIndex(printerList, i))); - list.append(QString::fromCFString(PMPrinterGetID(printer))); - } - } - return list; -} - -QString QCocoaPrinterSupport::defaultPrintDeviceId() const -{ - QCFType printerList; - if (PMServerCreatePrinterList(kPMServerLocal, &printerList) == noErr) { - CFIndex count = CFArrayGetCount(printerList); - for (CFIndex i = 0; i < count; ++i) { - PMPrinter printer = static_cast(const_cast(CFArrayGetValueAtIndex(printerList, i))); - if (PMPrinterIsDefault(printer)) - return QString::fromCFString(PMPrinterGetID(printer)); - } - } - return QString(); -} - -QT_END_NAMESPACE - -#endif //QT_NO_PRINTER diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac.mm b/src/plugins/platforms/cocoa/qpaintengine_mac.mm deleted file mode 100644 index b50c39c9b0..0000000000 --- a/src/plugins/platforms/cocoa/qpaintengine_mac.mm +++ /dev/null @@ -1,1428 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include - -#include "qpaintengine_mac_p.h" -#if defined(QT_PRINTSUPPORT_LIB) -#include "qprintengine_mac_p.h" -#endif - -#include -#include -#include -#include -#include -#if defined(QT_PRINTSUPPORT_LIB) -#include -#endif -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qcocoahelpers.h" - -#include - -QT_BEGIN_NAMESPACE - -/***************************************************************************** - QCoreGraphicsPaintEngine utility functions - *****************************************************************************/ - -void qt_mac_cgimage_data_free(void *, const void *memoryToFree, size_t) -{ - free(const_cast(memoryToFree)); -} - -CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr) -{ - QImage image = pixmap.toImage(); - if (image.format() != QImage::Format_ARGB32_Premultiplied) - image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); - - const int sx = qRound(sr.x()), sy = qRound(sr.y()), sw = qRound(sr.width()), sh = qRound(sr.height()); - const qsizetype sbpr = image.bytesPerLine(); - const uint nbytes = sw * sh; - // alpha is always 255 for bitmaps, ignore it in this case. - const quint32 mask = pixmap.depth() == 1 ? 0x00ffffff : 0xffffffff; - quint8 *dptr = static_cast(malloc(nbytes)); - quint32 *sptr = reinterpret_cast(image.scanLine(0)), *srow; - for (int y = sy, offset=0; y < sh; ++y) { - srow = sptr + (y * (sbpr / 4)); - for (int x = sx; x < sw; ++x) - *(dptr+(offset++)) = (*(srow+x) & mask) ? 255 : 0; - } - QCFType provider = CGDataProviderCreateWithData(nullptr, dptr, nbytes, qt_mac_cgimage_data_free); - return CGImageMaskCreate(sw, sh, 8, 8, nbytes / sh, provider, nullptr, false); -} - -//conversion -inline static float qt_mac_convert_color_to_cg(int c) { return ((float)c * 1000 / 255) / 1000; } -CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &t) { - return CGAffineTransformMake(t.m11(), t.m12(), t.m21(), t.m22(), t.dx(), t.dy()); -} - -inline static QCFType cgColorForQColor(const QColor &col) -{ - CGFloat components[] = { - qt_mac_convert_color_to_cg(col.red()), - qt_mac_convert_color_to_cg(col.green()), - qt_mac_convert_color_to_cg(col.blue()), - qt_mac_convert_color_to_cg(col.alpha()) - }; - QCFType colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - return CGColorCreate(colorSpace, components); -} - -// There's architectural problems with using native gradients -// on the Mac at the moment, so disable them. -// #define QT_MAC_USE_NATIVE_GRADIENTS - -#ifdef QT_MAC_USE_NATIVE_GRADIENTS -static bool drawGradientNatively(const QGradient *gradient) -{ - return gradient->spread() == QGradient::PadSpread; -} - -// gradiant callback -static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out) -{ - QBrush *brush = static_cast(info); - Q_ASSERT(brush && brush->gradient()); - - const QGradientStops stops = brush->gradient()->stops(); - const int n = stops.count(); - Q_ASSERT(n >= 1); - const QGradientStop *begin = stops.constBegin(); - const QGradientStop *end = begin + n; - - qreal p = in[0]; - const QGradientStop *i = begin; - while (i != end && i->first < p) - ++i; - - QRgb c; - if (i == begin) { - c = begin->second.rgba(); - } else if (i == end) { - c = (end - 1)->second.rgba(); - } else { - const QGradientStop &s1 = *(i - 1); - const QGradientStop &s2 = *i; - qreal p1 = s1.first; - qreal p2 = s2.first; - QRgb c1 = s1.second.rgba(); - QRgb c2 = s2.second.rgba(); - int idist = 256 * (p - p1) / (p2 - p1); - int dist = 256 - idist; - c = qRgba(INTERPOLATE_PIXEL_256(qRed(c1), dist, qRed(c2), idist), - INTERPOLATE_PIXEL_256(qGreen(c1), dist, qGreen(c2), idist), - INTERPOLATE_PIXEL_256(qBlue(c1), dist, qBlue(c2), idist), - INTERPOLATE_PIXEL_256(qAlpha(c1), dist, qAlpha(c2), idist)); - } - - out[0] = qt_mac_convert_color_to_cg(qRed(c)); - out[1] = qt_mac_convert_color_to_cg(qGreen(c)); - out[2] = qt_mac_convert_color_to_cg(qBlue(c)); - out[3] = qt_mac_convert_color_to_cg(qAlpha(c)); -} -#endif - -//clipping handling -void QCoreGraphicsPaintEnginePrivate::resetClip() -{ - static bool inReset = false; - if (inReset) - return; - inReset = true; - - CGAffineTransform old_xform = CGContextGetCTM(hd); - - //setup xforms - CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform)); - while (stackCount > 0) { - restoreGraphicsState(); - } - saveGraphicsState(); - inReset = false; - //reset xforms - CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd))); - CGContextConcatCTM(hd, old_xform); -} - -static CGRect qt_mac_compose_rect(const QRectF &r, float off=0) -{ - return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height()); -} - -static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0) -{ - CGMutablePathRef ret = CGPathCreateMutable(); - QPointF startPt; - for (int i=0; i 0 - && p.elementAt(i - 1).x == startPt.x() - && p.elementAt(i - 1).y == startPt.y()) - CGPathCloseSubpath(ret); - startPt = QPointF(elm.x, elm.y); - CGPathMoveToPoint(ret, 0, elm.x+off, elm.y+off); - break; - case QPainterPath::LineToElement: - CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off); - break; - case QPainterPath::CurveToElement: - Q_ASSERT(p.elementAt(i+1).type == QPainterPath::CurveToDataElement); - Q_ASSERT(p.elementAt(i+2).type == QPainterPath::CurveToDataElement); - CGPathAddCurveToPoint(ret, 0, - elm.x+off, elm.y+off, - p.elementAt(i+1).x+off, p.elementAt(i+1).y+off, - p.elementAt(i+2).x+off, p.elementAt(i+2).y+off); - i+=2; - break; - default: - qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type); - break; - } - } - if (!p.isEmpty() - && p.elementAt(p.elementCount() - 1).x == startPt.x() - && p.elementAt(p.elementCount() - 1).y == startPt.y()) - CGPathCloseSubpath(ret); - return ret; -} - -//pattern handling (tiling) -#if 1 -# define QMACPATTERN_MASK_MULTIPLIER 32 -#else -# define QMACPATTERN_MASK_MULTIPLIER 1 -#endif -class QMacPattern -{ -public: - QMacPattern() : as_mask(false), pdev(0), image(0) { data.bytes = 0; } - ~QMacPattern() { CGImageRelease(image); } - int width() { - if (image) - return CGImageGetWidth(image); - if (data.bytes) - return 8*QMACPATTERN_MASK_MULTIPLIER; - return data.pixmap.width(); - } - int height() { - if (image) - return CGImageGetHeight(image); - if (data.bytes) - return 8*QMACPATTERN_MASK_MULTIPLIER; - return data.pixmap.height(); - } - - //input - QColor foreground; - bool as_mask; - struct { - QPixmap pixmap; - const uchar *bytes; - } data; - QPaintDevice *pdev; - //output - CGImageRef image; -}; -static void qt_mac_draw_pattern(void *info, CGContextRef c) -{ - QMacPattern *pat = (QMacPattern*)info; - int w = 0, h = 0; - bool isBitmap = (pat->data.pixmap.depth() == 1); - if (!pat->image) { //lazy cache - if (pat->as_mask) { - Q_ASSERT(pat->data.bytes); - w = h = 8; -#if (QMACPATTERN_MASK_MULTIPLIER == 1) - CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, pat->data.bytes, w*h, nullptr); - pat->image = CGImageMaskCreate(w, h, 1, 1, 1, provider, nullptr, false); - CGDataProviderRelease(provider); -#else - const int numBytes = (w*h)/sizeof(uchar); - uchar xor_bytes[numBytes]; - for (int i = 0; i < numBytes; ++i) - xor_bytes[i] = pat->data.bytes[i] ^ 0xFF; - CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, xor_bytes, w*h, nullptr); - CGImageRef swatch = CGImageMaskCreate(w, h, 1, 1, 1, provider, nullptr, false); - CGDataProviderRelease(provider); - - const QColor c0(0, 0, 0, 0), c1(255, 255, 255, 255); - QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER); - pm.fill(c0); - QMacCGContext pm_ctx(&pm); - CGContextSetFillColorWithColor(c, cgColorForQColor(c1)); - CGRect rect = CGRectMake(0, 0, w, h); - for (int x = 0; x < QMACPATTERN_MASK_MULTIPLIER; ++x) { - rect.origin.x = x * w; - for (int y = 0; y < QMACPATTERN_MASK_MULTIPLIER; ++y) { - rect.origin.y = y * h; - qt_mac_drawCGImage(pm_ctx, &rect, swatch); - } - } - pat->image = qt_mac_create_imagemask(pm, pm.rect()); - CGImageRelease(swatch); - w *= QMACPATTERN_MASK_MULTIPLIER; - h *= QMACPATTERN_MASK_MULTIPLIER; -#endif - } else { - w = pat->data.pixmap.width(); - h = pat->data.pixmap.height(); - if (isBitmap) - pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect()); - else - pat->image = qt_mac_toCGImage(pat->data.pixmap.toImage()); - } - } else { - w = CGImageGetWidth(pat->image); - h = CGImageGetHeight(pat->image); - } - - //draw - bool needRestore = false; - if (CGImageIsMask(pat->image)) { - CGContextSaveGState(c); - CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground)); - } - CGRect rect = CGRectMake(0, 0, w, h); - qt_mac_drawCGImage(c, &rect, pat->image); - if (needRestore) - CGContextRestoreGState(c); -} -static void qt_mac_dispose_pattern(void *info) -{ - QMacPattern *pat = (QMacPattern*)info; - delete pat; -} - -/***************************************************************************** - QCoreGraphicsPaintEngine member functions - *****************************************************************************/ - -inline static QPaintEngine::PaintEngineFeatures qt_mac_cg_features() -{ - return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent - & ~QPaintEngine::PerspectiveTransform - & ~QPaintEngine::ConicalGradientFill - & ~QPaintEngine::LinearGradientFill - & ~QPaintEngine::RadialGradientFill - & ~QPaintEngine::BrushStroke); -} - -QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine() -: QPaintEngine(*(new QCoreGraphicsPaintEnginePrivate), qt_mac_cg_features()) -{ -} - -QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr) -: QPaintEngine(dptr, qt_mac_cg_features()) -{ -} - -QCoreGraphicsPaintEngine::~QCoreGraphicsPaintEngine() -{ -} - -bool -QCoreGraphicsPaintEngine::begin(QPaintDevice *pdev) -{ - Q_D(QCoreGraphicsPaintEngine); - if (isActive()) { // already active painting - qWarning("QCoreGraphicsPaintEngine::begin: Painter already active"); - return false; - } - - //initialization - d->pdev = pdev; - d->complexXForm = false; - d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth; - d->cosmeticPenSize = 1; - d->current.clipEnabled = false; - d->pixelSize = QPoint(1,1); - - if (pdev->devType() != QInternal::Printer) { - QMacCGContext ctx(pdev); - d->hd = CGContextRetain(ctx); - if (d->hd) { - d->saveGraphicsState(); - d->orig_xform = CGContextGetCTM(d->hd); - if (d->shading) { - CGShadingRelease(d->shading); - d->shading = nullptr; - } - d->setClip(nullptr); //clear the context's clipping - } - } - - setActive(true); - - if (d->pdev->devType() == QInternal::Widget) { // device is a widget - QWidget *w = (QWidget*)d->pdev; - bool unclipped = w->testAttribute(Qt::WA_PaintUnclipped); - - if ((w->windowType() == Qt::Desktop)) { - if (!unclipped) - qWarning("QCoreGraphicsPaintEngine::begin: Does not support clipped desktop on OS X"); - // ## need to do [qt_mac_window_for(w) makeKeyAndOrderFront]; (need to rename the file) - } else if (unclipped) { - qWarning("QCoreGraphicsPaintEngine::begin: Does not support unclipped painting"); - } - } else if (d->pdev->devType() == QInternal::Pixmap) { // device is a pixmap - QPixmap *pm = (QPixmap*)d->pdev; - if (pm->isNull()) { - qWarning("QCoreGraphicsPaintEngine::begin: Cannot paint null pixmap"); - end(); - return false; - } - } - - setDirty(QPaintEngine::DirtyPen); - setDirty(QPaintEngine::DirtyBrush); - setDirty(QPaintEngine::DirtyBackground); - setDirty(QPaintEngine::DirtyHints); - return true; -} - -bool -QCoreGraphicsPaintEngine::end() -{ - Q_D(QCoreGraphicsPaintEngine); - setActive(false); - if (d->pdev->devType() == QInternal::Widget && static_cast(d->pdev)->windowType() == Qt::Desktop) { - // ### need to do [qt_mac_window_for(static_cast(d->pdev)) orderOut]; (need to rename) - } - if (d->shading) { - CGShadingRelease(d->shading); - d->shading = 0; - } - d->pdev = nullptr; - if (d->hd) { - d->restoreGraphicsState(); - CGContextSynchronize(d->hd); - CGContextRelease(d->hd); - d->hd = nullptr; - } - return true; -} - -void -QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state) -{ - Q_D(QCoreGraphicsPaintEngine); - QPaintEngine::DirtyFlags flags = state.state(); - - if (flags & DirtyTransform) - updateMatrix(state.transform()); - - if (flags & DirtyClipEnabled) { - if (state.isClipEnabled()) - updateClipPath(painter()->clipPath(), Qt::ReplaceClip); - else - updateClipPath(QPainterPath(), Qt::NoClip); - } - - if (flags & DirtyClipPath) { - updateClipPath(state.clipPath(), state.clipOperation()); - } else if (flags & DirtyClipRegion) { - updateClipRegion(state.clipRegion(), state.clipOperation()); - } - - // If the clip has changed we need to update all other states - // too, since they are included in the system context on OSX, - // and changing the clip resets that context back to scratch. - if (flags & (DirtyClipPath | DirtyClipRegion | DirtyClipEnabled)) - flags |= AllDirty; - - if (flags & DirtyPen) - updatePen(state.pen()); - if (flags & (DirtyBrush|DirtyBrushOrigin)) - updateBrush(state.brush(), state.brushOrigin()); - if (flags & DirtyFont) - updateFont(state.font()); - if (flags & DirtyOpacity) - updateOpacity(state.opacity()); - if (flags & DirtyHints) - updateRenderHints(state.renderHints()); - if (flags & DirtyCompositionMode) - updateCompositionMode(state.compositionMode()); - - if (flags & (DirtyPen | DirtyTransform | DirtyHints)) { - if (!qt_pen_is_cosmetic(d->current.pen, state.renderHints())) { - d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticNone; - } else if (d->current.transform.m11() < d->current.transform.m22()-1.0 || - d->current.transform.m11() > d->current.transform.m22()+1.0) { - d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath; - d->cosmeticPenSize = d->adjustPenWidth(d->current.pen.widthF()); - if (!d->cosmeticPenSize) - d->cosmeticPenSize = 1.0; - } else { - d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth; - static const float sqrt2 = std::sqrt(2.0f); - qreal width = d->current.pen.widthF(); - if (!width) - width = 1; - d->cosmeticPenSize = std::sqrt(std::pow(d->pixelSize.y(), 2) + std::pow(d->pixelSize.x(), 2)) / sqrt2 * width; - } - } -} - -void -QCoreGraphicsPaintEngine::updatePen(const QPen &pen) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - d->current.pen = pen; - d->setStrokePen(pen); -} - -void -QCoreGraphicsPaintEngine::updateBrush(const QBrush &brush, const QPointF &brushOrigin) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - d->current.brush = brush; - -#ifdef QT_MAC_USE_NATIVE_GRADIENTS - // Quartz supports only pad spread - if (const QGradient *gradient = brush.gradient()) { - if (drawGradientNatively(gradient)) { - gccaps |= QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill; - } else { - gccaps &= ~(QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill); - } - } -#endif - - if (d->shading) { - CGShadingRelease(d->shading); - d->shading = nullptr; - } - d->setFillBrush(brushOrigin); -} - -void -QCoreGraphicsPaintEngine::updateOpacity(qreal opacity) -{ - Q_D(QCoreGraphicsPaintEngine); - CGContextSetAlpha(d->hd, opacity); -} - -void -QCoreGraphicsPaintEngine::updateFont(const QFont &) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - updatePen(d->current.pen); -} - -void -QCoreGraphicsPaintEngine::updateMatrix(const QTransform &transform) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - - if (qt_is_nan(transform.m11()) || qt_is_nan(transform.m12()) || qt_is_nan(transform.m13()) - || qt_is_nan(transform.m21()) || qt_is_nan(transform.m22()) || qt_is_nan(transform.m23()) - || qt_is_nan(transform.m31()) || qt_is_nan(transform.m32()) || qt_is_nan(transform.m33())) - return; - - d->current.transform = transform; - d->setTransform(transform.isIdentity() ? 0 : &transform); - d->complexXForm = (transform.m11() != 1 || transform.m22() != 1 - || transform.m12() != 0 || transform.m21() != 0); - d->pixelSize = d->devicePixelSize(d->hd); -} - -void -QCoreGraphicsPaintEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - if (op == Qt::NoClip) { - if (d->current.clipEnabled) { - d->current.clipEnabled = false; - d->current.clip = QRegion(); - d->setClip(nullptr); - } - } else { - if (!d->current.clipEnabled) - op = Qt::ReplaceClip; - d->current.clipEnabled = true; - QRegion clipRegion(p.toFillPolygon().toPolygon(), p.fillRule()); - if (op == Qt::ReplaceClip) { - d->current.clip = clipRegion; - d->setClip(nullptr); - if (p.isEmpty()) { - CGRect rect = CGRectMake(0, 0, 0, 0); - CGContextClipToRect(d->hd, rect); - } else { - CGMutablePathRef path = qt_mac_compose_path(p); - CGContextBeginPath(d->hd); - CGContextAddPath(d->hd, path); - if (p.fillRule() == Qt::WindingFill) - CGContextClip(d->hd); - else - CGContextEOClip(d->hd); - CGPathRelease(path); - } - } else if (op == Qt::IntersectClip) { - d->current.clip = d->current.clip.intersected(clipRegion); - d->setClip(&d->current.clip); - } - } -} - -void -QCoreGraphicsPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - if (op == Qt::NoClip) { - d->current.clipEnabled = false; - d->current.clip = QRegion(); - d->setClip(nullptr); - } else { - if (!d->current.clipEnabled) - op = Qt::ReplaceClip; - d->current.clipEnabled = true; - if (op == Qt::IntersectClip) - d->current.clip = d->current.clip.intersected(clipRegion); - else if (op == Qt::ReplaceClip) - d->current.clip = clipRegion; - d->setClip(&d->current.clip); - } -} - -void -QCoreGraphicsPaintEngine::drawPath(const QPainterPath &p) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - - if (state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - CGMutablePathRef path = qt_mac_compose_path(p); - uchar ops = QCoreGraphicsPaintEnginePrivate::CGStroke; - if (p.fillRule() == Qt::WindingFill) - ops |= QCoreGraphicsPaintEnginePrivate::CGFill; - else - ops |= QCoreGraphicsPaintEnginePrivate::CGEOFill; - CGContextBeginPath(d->hd); - d->drawPath(ops, path); - CGPathRelease(path); -} - -void -QCoreGraphicsPaintEngine::drawRects(const QRectF *rects, int rectCount) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - - if (state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - for (int i=0; idrawPath(QCoreGraphicsPaintEnginePrivate::CGFill|QCoreGraphicsPaintEnginePrivate::CGStroke, - path); - CGPathRelease(path); - } -} - -void -QCoreGraphicsPaintEngine::drawPoints(const QPointF *points, int pointCount) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - - if (state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - if (d->current.pen.capStyle() == Qt::FlatCap) - CGContextSetLineCap(d->hd, kCGLineCapSquare); - - CGMutablePathRef path = CGPathCreateMutable(); - for (int i=0; i < pointCount; i++) { - float x = points[i].x(), y = points[i].y(); - CGPathMoveToPoint(path, nullptr, x, y); - CGPathAddLineToPoint(path, nullptr, x+0.001, y); - } - - bool doRestore = false; - if (d->cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticNone && !(state->renderHints() & QPainter::Antialiasing)) { - //we don't want adjusted pens for point rendering - doRestore = true; - d->saveGraphicsState(); - CGContextSetLineWidth(d->hd, d->current.pen.widthF()); - } - d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path); - if (doRestore) - d->restoreGraphicsState(); - CGPathRelease(path); - if (d->current.pen.capStyle() == Qt::FlatCap) - CGContextSetLineCap(d->hd, kCGLineCapButt); -} - -void -QCoreGraphicsPaintEngine::drawEllipse(const QRectF &r) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - - if (state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - CGMutablePathRef path = CGPathCreateMutable(); - CGAffineTransform transform = CGAffineTransformMakeScale(r.width() / r.height(), 1); - CGPathAddArc(path, &transform,(r.x() + (r.width() / 2)) / (r.width() / r.height()), - r.y() + (r.height() / 2), r.height() / 2, 0, (2 * M_PI), false); - d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill | QCoreGraphicsPaintEnginePrivate::CGStroke, - path); - CGPathRelease(path); -} - -void -QCoreGraphicsPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - - if (state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - CGMutablePathRef path = CGPathCreateMutable(); - CGPathMoveToPoint(path, nullptr, points[0].x(), points[0].y()); - for (int x = 1; x < pointCount; ++x) - CGPathAddLineToPoint(path, nullptr, points[x].x(), points[x].y()); - if (mode != PolylineMode && points[0] != points[pointCount-1]) - CGPathAddLineToPoint(path, nullptr, points[0].x(), points[0].y()); - uint op = QCoreGraphicsPaintEnginePrivate::CGStroke; - if (mode != PolylineMode) - op |= mode == OddEvenMode ? QCoreGraphicsPaintEnginePrivate::CGEOFill - : QCoreGraphicsPaintEnginePrivate::CGFill; - d->drawPath(op, path); - CGPathRelease(path); -} - -void -QCoreGraphicsPaintEngine::drawLines(const QLineF *lines, int lineCount) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - - if (state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - CGMutablePathRef path = CGPathCreateMutable(); - for (int i = 0; i < lineCount; i++) { - const QPointF start = lines[i].p1(), end = lines[i].p2(); - CGPathMoveToPoint(path, nullptr, start.x(), start.y()); - CGPathAddLineToPoint(path, nullptr, end.x(), end.y()); - } - d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path); - CGPathRelease(path); -} - -void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - - if (state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - if (pm.isNull()) - return; - - bool differentSize = (QRectF(0, 0, pm.width(), pm.height()) != sr), doRestore = false; - CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); - QCFType image; - bool isBitmap = (pm.depth() == 1); - if (isBitmap) { - doRestore = true; - d->saveGraphicsState(); - - const QColor &col = d->current.pen.color(); - CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col)); - image = qt_mac_create_imagemask(pm, sr); - } else if (differentSize) { - QCFType img = qt_mac_toCGImage(pm.toImage()); - if (img) - image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height()))); - } else { - image = qt_mac_toCGImage(pm.toImage()); - } - qt_mac_drawCGImage(d->hd, &rect, image); - if (doRestore) - d->restoreGraphicsState(); -} - -void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr, - Qt::ImageConversionFlags flags) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_UNUSED(flags); - Q_ASSERT(isActive()); - - if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - QCFType cgimage = qt_mac_toCGImage(img); - CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); - if (QRectF(0, 0, img.width(), img.height()) != sr) - cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(), - sr.width(), sr.height())); - qt_mac_drawCGImage(d->hd, &rect, cgimage); -} - -void QCoreGraphicsPaintEngine::initialize() -{ -} - -void QCoreGraphicsPaintEngine::cleanup() -{ -} - -CGContextRef -QCoreGraphicsPaintEngine::handle() const -{ - return d_func()->hd; -} - -void -QCoreGraphicsPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, - const QPointF &p) -{ - Q_D(QCoreGraphicsPaintEngine); - Q_ASSERT(isActive()); - - if (state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - //save the old state - d->saveGraphicsState(); - - //setup the pattern - QMacPattern *qpattern = new QMacPattern; - qpattern->data.pixmap = pixmap; - qpattern->foreground = d->current.pen.color(); - qpattern->pdev = d->pdev; - CGPatternCallbacks callbks; - callbks.version = 0; - callbks.drawPattern = qt_mac_draw_pattern; - callbks.releaseInfo = qt_mac_dispose_pattern; - const int width = qpattern->width(), height = qpattern->height(); - CGAffineTransform trans = CGContextGetCTM(d->hd); - CGPatternRef pat = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height), - trans, width, height, - kCGPatternTilingNoDistortion, true, &callbks); - CGColorSpaceRef cs = CGColorSpaceCreatePattern(nullptr); - CGContextSetFillColorSpace(d->hd, cs); - CGFloat component = 1.0; //just one - CGContextSetFillPattern(d->hd, pat, &component); - CGSize phase = CGSizeApplyAffineTransform(CGSizeMake(-(p.x()-r.x()), -(p.y()-r.y())), trans); - CGContextSetPatternPhase(d->hd, phase); - - //fill the rectangle - CGRect mac_rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); - CGContextFillRect(d->hd, mac_rect); - - //restore the state - d->restoreGraphicsState(); - //cleanup - CGColorSpaceRelease(cs); - CGPatternRelease(pat); -} - -void QCoreGraphicsPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &item) -{ - Q_D(QCoreGraphicsPaintEngine); - if (d->current.transform.type() == QTransform::TxProject -#ifndef QMAC_NATIVE_GRADIENTS - || painter()->pen().brush().gradient() //Just let the base engine "emulate" the gradient -#endif - ) { - QPaintEngine::drawTextItem(pos, item); - return; - } - - if (state->compositionMode() == QPainter::CompositionMode_Destination) - return; - - const QTextItemInt &ti = static_cast(item); - - QPen oldPen = painter()->pen(); - QBrush oldBrush = painter()->brush(); - QPointF oldBrushOrigin = painter()->brushOrigin(); - updatePen(Qt::NoPen); - updateBrush(oldPen.brush(), QPointF(0, 0)); - - Q_ASSERT(type() == QPaintEngine::CoreGraphics); - - QFontEngine *fe = ti.fontEngine; - - const bool textAA = ((state->renderHints() & QPainter::TextAntialiasing) - && !(fe->fontDef.styleStrategy & QFont::NoAntialias)); - const bool lineAA = state->renderHints() & QPainter::Antialiasing; - if (textAA != lineAA) - CGContextSetShouldAntialias(d->hd, textAA); - - const bool smoothing = textAA && !(fe->fontDef.styleStrategy & QFont::NoSubpixelAntialias); - if (d->disabledSmoothFonts == smoothing) - CGContextSetShouldSmoothFonts(d->hd, smoothing); - - if (ti.glyphs.numGlyphs) { - switch (fe->type()) { - case QFontEngine::Mac: - static_cast(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height()); - break; - case QFontEngine::Box: - d->drawBoxTextItem(pos, ti); - break; - default: - break; - } - } - - if (textAA != lineAA) - CGContextSetShouldAntialias(d->hd, !textAA); - - if (smoothing == d->disabledSmoothFonts) - CGContextSetShouldSmoothFonts(d->hd, !d->disabledSmoothFonts); - - updatePen(oldPen); - updateBrush(oldBrush, oldBrushOrigin); -} - -QPainter::RenderHints -QCoreGraphicsPaintEngine::supportedRenderHints() const -{ - return QPainter::RenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); -} -enum CGCompositeMode { - kCGCompositeModeClear = 0, - kCGCompositeModeCopy = 1, - kCGCompositeModeSourceOver = 2, - kCGCompositeModeSourceIn = 3, - kCGCompositeModeSourceOut = 4, - kCGCompositeModeSourceAtop = 5, - kCGCompositeModeDestinationOver = 6, - kCGCompositeModeDestinationIn = 7, - kCGCompositeModeDestinationOut = 8, - kCGCompositeModeDestinationAtop = 9, - kCGCompositeModeXOR = 10, - kCGCompositeModePlusDarker = 11, // (max (0, (1-d) + (1-s))) - kCGCompositeModePlusLighter = 12, // (min (1, s + d)) - }; -extern "C" { - extern void CGContextSetCompositeOperation(CGContextRef, int); -} // private function, but is in all versions of OS X. -void -QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) -{ - int cg_mode = kCGBlendModeNormal; - switch (mode) { - case QPainter::CompositionMode_Multiply: - cg_mode = kCGBlendModeMultiply; - break; - case QPainter::CompositionMode_Screen: - cg_mode = kCGBlendModeScreen; - break; - case QPainter::CompositionMode_Overlay: - cg_mode = kCGBlendModeOverlay; - break; - case QPainter::CompositionMode_Darken: - cg_mode = kCGBlendModeDarken; - break; - case QPainter::CompositionMode_Lighten: - cg_mode = kCGBlendModeLighten; - break; - case QPainter::CompositionMode_ColorDodge: - cg_mode = kCGBlendModeColorDodge; - break; - case QPainter::CompositionMode_ColorBurn: - cg_mode = kCGBlendModeColorBurn; - break; - case QPainter::CompositionMode_HardLight: - cg_mode = kCGBlendModeHardLight; - break; - case QPainter::CompositionMode_SoftLight: - cg_mode = kCGBlendModeSoftLight; - break; - case QPainter::CompositionMode_Difference: - cg_mode = kCGBlendModeDifference; - break; - case QPainter::CompositionMode_Exclusion: - cg_mode = kCGBlendModeExclusion; - break; - case QPainter::CompositionMode_Plus: - cg_mode = kCGBlendModePlusLighter; - break; - case QPainter::CompositionMode_SourceOver: - cg_mode = kCGBlendModeNormal; - break; - case QPainter::CompositionMode_DestinationOver: - cg_mode = kCGBlendModeDestinationOver; - break; - case QPainter::CompositionMode_Clear: - cg_mode = kCGBlendModeClear; - break; - case QPainter::CompositionMode_Source: - cg_mode = kCGBlendModeCopy; - break; - case QPainter::CompositionMode_Destination: - cg_mode = -1; - break; - case QPainter::CompositionMode_SourceIn: - cg_mode = kCGBlendModeSourceIn; - break; - case QPainter::CompositionMode_DestinationIn: - cg_mode = kCGCompositeModeDestinationIn; - break; - case QPainter::CompositionMode_SourceOut: - cg_mode = kCGBlendModeSourceOut; - break; - case QPainter::CompositionMode_DestinationOut: - cg_mode = kCGBlendModeDestinationOver; - break; - case QPainter::CompositionMode_SourceAtop: - cg_mode = kCGBlendModeSourceAtop; - break; - case QPainter::CompositionMode_DestinationAtop: - cg_mode = kCGBlendModeDestinationAtop; - break; - case QPainter::CompositionMode_Xor: - cg_mode = kCGBlendModeXOR; - break; - default: - break; - } - if (cg_mode > -1) { - CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode)); - } -} - -void -QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints) -{ - Q_D(QCoreGraphicsPaintEngine); - CGContextSetShouldAntialias(d->hd, hints & QPainter::Antialiasing); - CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? - kCGInterpolationHigh : kCGInterpolationNone); - bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing; - if (!textAntialiasing || d->disabledSmoothFonts) { - d->disabledSmoothFonts = !textAntialiasing; - CGContextSetShouldSmoothFonts(d->hd, textAntialiasing); - } -} - -/* - Returns the size of one device pixel in user-space coordinates. -*/ -QPointF QCoreGraphicsPaintEnginePrivate::devicePixelSize(CGContextRef) -{ - QPointF p1 = current.transform.inverted().map(QPointF(0, 0)); - QPointF p2 = current.transform.inverted().map(QPointF(1, 1)); - return QPointF(qAbs(p2.x() - p1.x()), qAbs(p2.y() - p1.y())); -} - -/* - Adjusts the pen width so we get correct line widths in the - non-transformed, aliased case. -*/ -float QCoreGraphicsPaintEnginePrivate::adjustPenWidth(float penWidth) -{ - Q_Q(QCoreGraphicsPaintEngine); - float ret = penWidth; - if (!complexXForm && !(q->state->renderHints() & QPainter::Antialiasing)) { - if (penWidth < 2) - ret = 1; - else if (penWidth < 3) - ret = 1.5; - else - ret = penWidth -1; - } - return ret; -} - -void -QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen) -{ - //pencap - CGLineCap cglinecap = kCGLineCapButt; - if (pen.capStyle() == Qt::SquareCap) - cglinecap = kCGLineCapSquare; - else if (pen.capStyle() == Qt::RoundCap) - cglinecap = kCGLineCapRound; - CGContextSetLineCap(hd, cglinecap); - CGContextSetLineWidth(hd, adjustPenWidth(pen.widthF())); - - //join - CGLineJoin cglinejoin = kCGLineJoinMiter; - if (pen.joinStyle() == Qt::BevelJoin) - cglinejoin = kCGLineJoinBevel; - else if (pen.joinStyle() == Qt::RoundJoin) - cglinejoin = kCGLineJoinRound; - CGContextSetLineJoin(hd, cglinejoin); -// CGContextSetMiterLimit(hd, pen.miterLimit()); - - //pen style - QVector linedashes; - if (pen.style() == Qt::CustomDashLine) { - QVector customs = pen.dashPattern(); - for (int i = 0; i < customs.size(); ++i) - linedashes.append(customs.at(i)); - } else if (pen.style() == Qt::DashLine) { - linedashes.append(4); - linedashes.append(2); - } else if (pen.style() == Qt::DotLine) { - linedashes.append(1); - linedashes.append(2); - } else if (pen.style() == Qt::DashDotLine) { - linedashes.append(4); - linedashes.append(2); - linedashes.append(1); - linedashes.append(2); - } else if (pen.style() == Qt::DashDotDotLine) { - linedashes.append(4); - linedashes.append(2); - linedashes.append(1); - linedashes.append(2); - linedashes.append(1); - linedashes.append(2); - } - const CGFloat cglinewidth = pen.widthF() <= 0.0f ? 1.0f : float(pen.widthF()); - for (int i = 0; i < linedashes.size(); ++i) { - linedashes[i] *= cglinewidth; - if (cglinewidth < 3 && (cglinecap == kCGLineCapSquare || cglinecap == kCGLineCapRound)) { - if ((i%2)) - linedashes[i] += cglinewidth/2; - else - linedashes[i] -= cglinewidth/2; - } - } - CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size()); - - // color - CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color())); -} - -// Add our own patterns here to deal with the fact that the coordinate system -// is flipped vertically with Quartz2D. -static const uchar *qt_mac_patternForBrush(int brushStyle) -{ - Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern); - static const uchar dense1_pat[] = { 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00 }; - static const uchar dense2_pat[] = { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 }; - static const uchar dense3_pat[] = { 0x11, 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa }; - static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }; - static const uchar dense5_pat[] = { 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55 }; - static const uchar dense6_pat[] = { 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 }; - static const uchar dense7_pat[] = { 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff }; - static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff }; - static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef }; - static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef }; - static const uchar fdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe }; - static const uchar bdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f }; - static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e }; - static const uchar *const pat_tbl[] = { - dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat, - dense6_pat, dense7_pat, - hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat }; - return pat_tbl[brushStyle - Qt::Dense1Pattern]; -} - -void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset) -{ - // pattern - Qt::BrushStyle bs = current.brush.style(); -#ifdef QT_MAC_USE_NATIVE_GRADIENTS - if (bs == Qt::LinearGradientPattern || bs == Qt::RadialGradientPattern) { - const QGradient *grad = static_cast(current.brush.gradient()); - if (drawGradientNatively(grad)) { - Q_ASSERT(grad->spread() == QGradient::PadSpread); - - static const CGFloat domain[] = { 0.0f, +1.0f }; - static const CGFunctionCallbacks callbacks = { 0, qt_mac_color_gradient_function, nullptr }; - CGFunctionRef fill_func = CGFunctionCreate(reinterpret_cast(¤t.brush), - 1, domain, 4, nullptr, &callbacks); - - CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB) - if (bs == Qt::LinearGradientPattern) { - const QLinearGradient *linearGrad = static_cast(grad); - const QPointF start(linearGrad->start()); - const QPointF stop(linearGrad->finalStop()); - shading = CGShadingCreateAxial(colorspace, CGPointMake(start.x(), start.y()), - CGPointMake(stop.x(), stop.y()), fill_func, true, true); - } else { - Q_ASSERT(bs == Qt::RadialGradientPattern); - const QRadialGradient *radialGrad = static_cast(grad); - QPointF center(radialGrad->center()); - QPointF focal(radialGrad->focalPoint()); - qreal radius = radialGrad->radius(); - qreal focalRadius = radialGrad->focalRadius(); - shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()), - focalRadius, CGPointMake(center.x(), center.y()), radius, fill_func, false, true); - } - - CGFunctionRelease(fill_func); - } - } else -#endif - if (bs != Qt::SolidPattern && bs != Qt::NoBrush -#ifndef QT_MAC_USE_NATIVE_GRADIENTS - && (bs < Qt::LinearGradientPattern || bs > Qt::ConicalGradientPattern) -#endif - ) - { - QMacPattern *qpattern = new QMacPattern; - qpattern->pdev = pdev; - CGFloat components[4] = { 1.0, 1.0, 1.0, 1.0 }; - CGColorSpaceRef base_colorspace = nullptr; - if (bs == Qt::TexturePattern) { - qpattern->data.pixmap = current.brush.texture(); - if (qpattern->data.pixmap.isQBitmap()) { - const QColor &col = current.brush.color(); - components[0] = qt_mac_convert_color_to_cg(col.red()); - components[1] = qt_mac_convert_color_to_cg(col.green()); - components[2] = qt_mac_convert_color_to_cg(col.blue()); - base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - } - } else { - qpattern->as_mask = true; - - qpattern->data.bytes = qt_mac_patternForBrush(bs); - const QColor &col = current.brush.color(); - components[0] = qt_mac_convert_color_to_cg(col.red()); - components[1] = qt_mac_convert_color_to_cg(col.green()); - components[2] = qt_mac_convert_color_to_cg(col.blue()); - base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - } - int width = qpattern->width(), height = qpattern->height(); - qpattern->foreground = current.brush.color(); - - CGColorSpaceRef fill_colorspace = CGColorSpaceCreatePattern(base_colorspace); - CGContextSetFillColorSpace(hd, fill_colorspace); - - CGAffineTransform xform = CGContextGetCTM(hd); - xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(current.brush.transform()), xform); - xform = CGAffineTransformTranslate(xform, offset.x(), offset.y()); - - CGPatternCallbacks callbks; - callbks.version = 0; - callbks.drawPattern = qt_mac_draw_pattern; - callbks.releaseInfo = qt_mac_dispose_pattern; - CGPatternRef fill_pattern = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height), - xform, width, height, kCGPatternTilingNoDistortion, - !base_colorspace, &callbks); - CGContextSetFillPattern(hd, fill_pattern, components); - - - CGPatternRelease(fill_pattern); - CGColorSpaceRelease(base_colorspace); - CGColorSpaceRelease(fill_colorspace); - } else if (bs != Qt::NoBrush) { - CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color())); - } -} - -void -QCoreGraphicsPaintEnginePrivate::setClip(const QRegion *rgn) -{ - Q_Q(QCoreGraphicsPaintEngine); - if (hd) { - resetClip(); - QRegion sysClip = q->systemClip(); - if (!sysClip.isEmpty()) - qt_mac_clip_cg(hd, sysClip, &orig_xform); - if (rgn) - qt_mac_clip_cg(hd, *rgn, nullptr); - } -} - -struct qt_mac_cg_transform_path { - CGMutablePathRef path; - CGAffineTransform transform; -}; - -void qt_mac_cg_transform_path_apply(void *info, const CGPathElement *element) -{ - Q_ASSERT(info && element); - qt_mac_cg_transform_path *t = (qt_mac_cg_transform_path*)info; - switch (element->type) { - case kCGPathElementMoveToPoint: - CGPathMoveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y); - break; - case kCGPathElementAddLineToPoint: - CGPathAddLineToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y); - break; - case kCGPathElementAddQuadCurveToPoint: - CGPathAddQuadCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y, - element->points[1].x, element->points[1].y); - break; - case kCGPathElementAddCurveToPoint: - CGPathAddCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y, - element->points[1].x, element->points[1].y, - element->points[2].x, element->points[2].y); - break; - case kCGPathElementCloseSubpath: - CGPathCloseSubpath(t->path); - break; - default: - qDebug() << "Unhandled path transform type: " << element->type; - } -} - -void QCoreGraphicsPaintEnginePrivate::drawPath(uchar ops, CGMutablePathRef path) -{ - Q_Q(QCoreGraphicsPaintEngine); - Q_ASSERT((ops & (CGFill | CGEOFill)) != (CGFill | CGEOFill)); //can't really happen - if ((ops & (CGFill | CGEOFill))) { - if (shading) { - Q_ASSERT(path); - CGContextBeginPath(hd); - CGContextAddPath(hd, path); - saveGraphicsState(); - if (ops & CGFill) - CGContextClip(hd); - else if (ops & CGEOFill) - CGContextEOClip(hd); - if (current.brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode) { - CGRect boundingBox = CGPathGetBoundingBox(path); - CGContextConcatCTM(hd, - CGAffineTransformMake(boundingBox.size.width, 0, - 0, boundingBox.size.height, - boundingBox.origin.x, boundingBox.origin.y)); - } - CGContextDrawShading(hd, shading); - restoreGraphicsState(); - ops &= ~CGFill; - ops &= ~CGEOFill; - } else if (current.brush.style() == Qt::NoBrush) { - ops &= ~CGFill; - ops &= ~CGEOFill; - } - } - if ((ops & CGStroke) && current.pen.style() == Qt::NoPen) - ops &= ~CGStroke; - - if (ops & (CGEOFill | CGFill)) { - CGContextBeginPath(hd); - CGContextAddPath(hd, path); - if (ops & CGEOFill) { - CGContextEOFillPath(hd); - } else { - CGContextFillPath(hd); - } - } - - // Avoid saving and restoring the context if we can. - const bool needContextSave = (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone || - !(q->state->renderHints() & QPainter::Antialiasing)); - if (ops & CGStroke) { - if (needContextSave) - saveGraphicsState(); - CGContextBeginPath(hd); - - // Translate a fraction of a pixel size in the y direction - // to make sure that primitives painted at pixel borders - // fills the right pixel. This is needed since the y xais - // in the Quartz coordinate system is inverted compared to Qt. - if (!(q->state->renderHints() & QPainter::Antialiasing)) { - if (current.pen.style() == Qt::SolidLine || current.pen.width() >= 3) - CGContextTranslateCTM(hd, double(pixelSize.x()) * 0.25, double(pixelSize.y()) * 0.25); - else - CGContextTranslateCTM(hd, 0, double(pixelSize.y()) * 0.1); - } - - if (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone) { - // If antialiazing is enabled, use the cosmetic pen size directly. - if (q->state->renderHints() & QPainter::Antialiasing) - CGContextSetLineWidth(hd, cosmeticPenSize); - else if (current.pen.widthF() <= 1) - CGContextSetLineWidth(hd, cosmeticPenSize * 0.9f); - else - CGContextSetLineWidth(hd, cosmeticPenSize); - } - if (cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath) { - qt_mac_cg_transform_path t; - t.transform = qt_mac_convert_transform_to_cg(current.transform); - t.path = CGPathCreateMutable(); - CGPathApply(path, &t, qt_mac_cg_transform_path_apply); //transform the path - setTransform(nullptr); //unset the context transform - CGContextSetLineWidth(hd, cosmeticPenSize); - CGContextAddPath(hd, t.path); - CGPathRelease(t.path); - } else { - CGContextAddPath(hd, path); - } - - CGContextStrokePath(hd); - if (needContextSave) - restoreGraphicsState(); - } -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac_p.h b/src/plugins/platforms/cocoa/qpaintengine_mac_p.h deleted file mode 100644 index a52e9cbe1c..0000000000 --- a/src/plugins/platforms/cocoa/qpaintengine_mac_p.h +++ /dev/null @@ -1,205 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPAINTENGINE_MAC_P_H -#define QPAINTENGINE_MAC_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 -#include -#include -#include -#include - -typedef struct CGColorSpace *CGColorSpaceRef; -typedef struct CGContext *CGContextRef; - -QT_BEGIN_NAMESPACE - -class QCoreGraphicsPaintEnginePrivate; -class QCoreGraphicsPaintEngine : public QPaintEngine -{ - Q_DECLARE_PRIVATE(QCoreGraphicsPaintEngine) - -public: - QCoreGraphicsPaintEngine(); - ~QCoreGraphicsPaintEngine(); - - bool begin(QPaintDevice *pdev); - bool end(); - - void updateState(const QPaintEngineState &state); - - void updatePen(const QPen &pen); - void updateBrush(const QBrush &brush, const QPointF &pt); - void updateFont(const QFont &font); - void updateOpacity(qreal opacity); - void updateMatrix(const QTransform &matrix); - void updateTransform(const QTransform &matrix); - void updateClipRegion(const QRegion ®ion, Qt::ClipOperation op); - void updateClipPath(const QPainterPath &path, Qt::ClipOperation op); - void updateCompositionMode(QPainter::CompositionMode mode); - void updateRenderHints(QPainter::RenderHints hints); - - void drawLines(const QLineF *lines, int lineCount); - void drawRects(const QRectF *rects, int rectCount); - void drawPoints(const QPointF *p, int pointCount); - void drawEllipse(const QRectF &r); - void drawPath(const QPainterPath &path); - - void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); - void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); - void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); - - void drawTextItem(const QPointF &pos, const QTextItem &item); - void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, - Qt::ImageConversionFlags flags = Qt::AutoColor); - - Type type() const { return QPaintEngine::CoreGraphics; } - - CGContextRef handle() const; - - static void initialize(); - static void cleanup(); - - QPainter::RenderHints supportedRenderHints() const; - - //avoid partial shadowed overload warnings... - void drawLines(const QLine *lines, int lineCount) { QPaintEngine::drawLines(lines, lineCount); } - void drawRects(const QRect *rects, int rectCount) { QPaintEngine::drawRects(rects, rectCount); } - void drawPoints(const QPoint *p, int pointCount) { QPaintEngine::drawPoints(p, pointCount); } - void drawEllipse(const QRect &r) { QPaintEngine::drawEllipse(r); } - void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) - { QPaintEngine::drawPolygon(points, pointCount, mode); } - -protected: - friend class QMacPrintEngine; - friend class QMacPrintEnginePrivate; - QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr); - -private: - Q_DISABLE_COPY(QCoreGraphicsPaintEngine) -}; - -/***************************************************************************** - Private data - *****************************************************************************/ -class QCoreGraphicsPaintEnginePrivate : public QPaintEnginePrivate -{ - Q_DECLARE_PUBLIC(QCoreGraphicsPaintEngine) -public: - QCoreGraphicsPaintEnginePrivate() - : hd(nullptr), shading(nullptr), stackCount(0), complexXForm(false), disabledSmoothFonts(false) - { - } - - struct { - QPen pen; - QBrush brush; - uint clipEnabled : 1; - QRegion clip; - QTransform transform; - } current; - - //state info (shared with QD) - CGAffineTransform orig_xform; - - //cg structures - CGContextRef hd; - CGShadingRef shading; - int stackCount; - bool complexXForm; - bool disabledSmoothFonts; - enum { CosmeticNone, CosmeticTransformPath, CosmeticSetPenWidth } cosmeticPen; - - // pixel and cosmetic pen size in user coordinates. - QPointF pixelSize; - float cosmeticPenSize; - - //internal functions - enum { CGStroke=0x01, CGEOFill=0x02, CGFill=0x04 }; - void drawPath(uchar ops, CGMutablePathRef path = nullptr); - void setClip(const QRegion *rgn = nullptr); - void resetClip(); - void setFillBrush(const QPointF &origin=QPoint()); - void setStrokePen(const QPen &pen); - inline void saveGraphicsState(); - inline void restoreGraphicsState(); - float penOffset(); - QPointF devicePixelSize(CGContextRef context); - float adjustPenWidth(float penWidth); - inline void setTransform(const QTransform *matrix = nullptr) - { - CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd))); - CGAffineTransform xform = orig_xform; - if (matrix) { - extern CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &); - xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(*matrix), xform); - } - CGContextConcatCTM(hd, xform); - CGContextSetTextMatrix(hd, xform); - } -}; - -inline void QCoreGraphicsPaintEnginePrivate::saveGraphicsState() -{ - ++stackCount; - CGContextSaveGState(hd); -} - -inline void QCoreGraphicsPaintEnginePrivate::restoreGraphicsState() -{ - --stackCount; - Q_ASSERT(stackCount >= 0); - CGContextRestoreGState(hd); -} - -QT_END_NAMESPACE - -#endif // QPAINTENGINE_MAC_P_H diff --git a/src/plugins/platforms/cocoa/qprintengine_mac.mm b/src/plugins/platforms/cocoa/qprintengine_mac.mm deleted file mode 100644 index 9391f921ec..0000000000 --- a/src/plugins/platforms/cocoa/qprintengine_mac.mm +++ /dev/null @@ -1,797 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include - -#include "qprintengine_mac_p.h" -#include "qcocoaprintersupport.h" -#include -#include -#include -#include - -#include - -#ifndef QT_NO_PRINTER - -QT_BEGIN_NAMESPACE - -extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits); - -QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode, const QString &deviceId) - : QPaintEngine(*(new QMacPrintEnginePrivate)) -{ - Q_D(QMacPrintEngine); - d->mode = mode; - QString id = deviceId; - if (id.isEmpty()) - id = QCocoaPrinterSupport().defaultPrintDeviceId(); - else - setProperty(QPrintEngine::PPK_PrinterName, deviceId); - d->m_printDevice.reset(new QCocoaPrintDevice(id)); - d->m_pageLayout.setPageSize(d->m_printDevice->defaultPageSize()); - d->initialize(); -} - -bool QMacPrintEngine::begin(QPaintDevice *dev) -{ - Q_D(QMacPrintEngine); - - Q_ASSERT(dev && dev->devType() == QInternal::Printer); - if (!static_cast(dev)->isValid()) - return false; - - if (d->state == QPrinter::Idle && !d->isPrintSessionInitialized()) // Need to reinitialize - d->initialize(); - - d->paintEngine->state = state; - d->paintEngine->begin(dev); - Q_ASSERT_X(d->state == QPrinter::Idle, "QMacPrintEngine", "printer already active"); - - if (PMSessionValidatePrintSettings(d->session(), d->settings(), kPMDontWantBoolean) != noErr - || PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean) != noErr) { - d->state = QPrinter::Error; - return false; - } - - if (!d->outputFilename.isEmpty()) { - QCFType outFile = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, - QCFString(d->outputFilename), - kCFURLPOSIXPathStyle, - false); - if (PMSessionSetDestination(d->session(), d->settings(), kPMDestinationFile, - kPMDocumentFormatPDF, outFile) != noErr) { - qWarning("QMacPrintEngine::begin: Problem setting file [%s]", d->outputFilename.toUtf8().constData()); - return false; - } - } - - OSStatus status = PMSessionBeginCGDocumentNoDialog(d->session(), d->settings(), d->format()); - if (status != noErr) { - d->state = QPrinter::Error; - return false; - } - - d->state = QPrinter::Active; - setActive(true); - d->newPage_helper(); - return true; -} - -bool QMacPrintEngine::end() -{ - Q_D(QMacPrintEngine); - if (d->state == QPrinter::Aborted) - return true; // I was just here a function call ago :) - if (d->paintEngine->type() == QPaintEngine::CoreGraphics) { - // We don't need the paint engine to call restoreGraphicsState() - static_cast(d->paintEngine)->d_func()->stackCount = 0; - static_cast(d->paintEngine)->d_func()->hd = nullptr; - } - d->paintEngine->end(); - if (d->state != QPrinter::Idle) - d->releaseSession(); - d->state = QPrinter::Idle; - return true; -} - -QPaintEngine * -QMacPrintEngine::paintEngine() const -{ - return d_func()->paintEngine; -} - -Qt::HANDLE QMacPrintEngine::handle() const -{ - QCoreGraphicsPaintEngine *cgEngine = static_cast(paintEngine()); - return cgEngine->d_func()->hd; -} - -QMacPrintEnginePrivate::~QMacPrintEnginePrivate() -{ - [printInfo release]; - delete paintEngine; -} - -QPrinter::PrinterState QMacPrintEngine::printerState() const -{ - return d_func()->state; -} - -bool QMacPrintEngine::newPage() -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - OSStatus err = PMSessionEndPageNoDialog(d->session()); - if (err != noErr) { - if (err == kPMCancel) { - // User canceled, we need to abort! - abort(); - } else { - // Not sure what the problem is... - qWarning("QMacPrintEngine::newPage: Cannot end current page. %ld", long(err)); - d->state = QPrinter::Error; - } - return false; - } - return d->newPage_helper(); -} - -bool QMacPrintEngine::abort() -{ - Q_D(QMacPrintEngine); - if (d->state != QPrinter::Active) - return false; - bool ret = end(); - d->state = QPrinter::Aborted; - return ret; -} - -int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const -{ - Q_D(const QMacPrintEngine); - int val = 1; - switch (m) { - case QPaintDevice::PdmWidth: - val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).width(); - break; - case QPaintDevice::PdmHeight: - val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).height(); - break; - case QPaintDevice::PdmWidthMM: - val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).width()); - break; - case QPaintDevice::PdmHeightMM: - val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).height()); - break; - case QPaintDevice::PdmPhysicalDpiX: - case QPaintDevice::PdmPhysicalDpiY: { - PMPrinter printer; - if (PMSessionGetCurrentPrinter(d->session(), &printer) == noErr) { - PMResolution resolution; - PMPrinterGetOutputResolution(printer, d->settings(), &resolution); - val = (int)resolution.vRes; - break; - } - Q_FALLTHROUGH(); - } - case QPaintDevice::PdmDpiY: - val = (int)d->resolution.vRes; - break; - case QPaintDevice::PdmDpiX: - val = (int)d->resolution.hRes; - break; - case QPaintDevice::PdmNumColors: - val = (1 << metric(QPaintDevice::PdmDepth)); - break; - case QPaintDevice::PdmDepth: - val = 24; - break; - case QPaintDevice::PdmDevicePixelRatio: - val = 1; - break; - case QPaintDevice::PdmDevicePixelRatioScaled: - val = 1 * QPaintDevice::devicePixelRatioFScale(); - break; - default: - val = 0; - qWarning("QPrinter::metric: Invalid metric command"); - } - return val; -} - -void QMacPrintEnginePrivate::initialize() -{ - Q_Q(QMacPrintEngine); - - Q_ASSERT(!printInfo); - - if (!paintEngine) - paintEngine = new QCoreGraphicsPaintEngine(); - - q->gccaps = paintEngine->gccaps; - - QMacAutoReleasePool pool; - printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]]; - - QList resolutions = m_printDevice->supportedResolutions(); - if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) { - std::sort(resolutions.begin(), resolutions.end()); - if (resolutions.count() > 1 && mode == QPrinter::HighResolution) - resolution.hRes = resolution.vRes = resolutions.last(); - else - resolution.hRes = resolution.vRes = resolutions.first(); - if (resolution.hRes == 0) - resolution.hRes = resolution.vRes = 600; - } else { - resolution.hRes = resolution.vRes = qt_defaultDpi(); - } - - setPageSize(m_pageLayout.pageSize()); - - QHash::const_iterator propC; - for (propC = valueCache.constBegin(); propC != valueCache.constEnd(); ++propC) { - q->setProperty(propC.key(), propC.value()); - } -} - -void QMacPrintEnginePrivate::releaseSession() -{ - PMSessionEndPageNoDialog(session()); - PMSessionEndDocumentNoDialog(session()); - [printInfo release]; - printInfo = nil; -} - -bool QMacPrintEnginePrivate::newPage_helper() -{ - Q_Q(QMacPrintEngine); - Q_ASSERT(state == QPrinter::Active); - - if (PMSessionError(session()) != noErr) { - q->abort(); - return false; - } - - // pop the stack of saved graphic states, in case we get the same - // context back - either way, the stack count should be 0 when we - // get the new one - QCoreGraphicsPaintEngine *cgEngine = static_cast(paintEngine); - while (cgEngine->d_func()->stackCount > 0) - cgEngine->d_func()->restoreGraphicsState(); - - OSStatus status = PMSessionBeginPageNoDialog(session(), format(), nullptr); - if (status != noErr) { - state = QPrinter::Error; - return false; - } - - QRect page = m_pageLayout.paintRectPixels(resolution.hRes); - QRect paper = m_pageLayout.fullRectPixels(resolution.hRes); - - CGContextRef cgContext; - OSStatus err = noErr; - err = PMSessionGetCGGraphicsContext(session(), &cgContext); - if (err != noErr) { - qWarning("QMacPrintEngine::newPage: Cannot retrieve CoreGraphics context: %ld", long(err)); - state = QPrinter::Error; - return false; - } - cgEngine->d_func()->hd = cgContext; - - // Set the resolution as a scaling ration of 72 (the default). - CGContextScaleCTM(cgContext, 72 / resolution.hRes, 72 / resolution.vRes); - - CGContextScaleCTM(cgContext, 1, -1); - CGContextTranslateCTM(cgContext, 0, -paper.height()); - if (m_pageLayout.mode() != QPageLayout::FullPageMode) - CGContextTranslateCTM(cgContext, page.x() - paper.x(), page.y() - paper.y()); - cgEngine->d_func()->orig_xform = CGContextGetCTM(cgContext); - cgEngine->d_func()->setClip(nullptr); - cgEngine->state->dirtyFlags = QPaintEngine::DirtyFlag(QPaintEngine::AllDirty - & ~(QPaintEngine::DirtyClipEnabled - | QPaintEngine::DirtyClipRegion - | QPaintEngine::DirtyClipPath)); - if (cgEngine->painter()->hasClipping()) - cgEngine->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled; - cgEngine->syncState(); - return true; -} - -void QMacPrintEnginePrivate::setPageSize(const QPageSize &pageSize) -{ - if (!pageSize.isValid()) - return; - - // Get the matching printer paper - QPageSize printerPageSize = m_printDevice->supportedPageSize(pageSize); - QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize; - - // Get the PMPaper and check it is valid - PMPaper macPaper = m_printDevice->macPaper(usePageSize); - if (!macPaper) { - qWarning() << "QMacPrintEngine: Invalid PMPaper returned for " << pageSize; - return; - } - - QMarginsF printable = m_printDevice->printableMargins(usePageSize, m_pageLayout.orientation(), resolution.hRes); - m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units())); - - // You cannot set the page size on a PMPageFormat, you must create a new PMPageFormat - PMPageFormat pageFormat; - PMCreatePageFormatWithPMPaper(&pageFormat, macPaper); - PMSetOrientation(pageFormat, m_pageLayout.orientation() == QPageLayout::Landscape ? kPMLandscape : kPMPortrait, kPMUnlocked); - PMCopyPageFormat(pageFormat, format()); - if (PMSessionValidatePageFormat(session(), format(), kPMDontWantBoolean) != noErr) - qWarning("QMacPrintEngine: Invalid page format"); - PMRelease(pageFormat); -} - -void QMacPrintEngine::updateState(const QPaintEngineState &state) -{ - d_func()->paintEngine->updateState(state); -} - -void QMacPrintEngine::drawRects(const QRectF *r, int num) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - d->paintEngine->drawRects(r, num); -} - -void QMacPrintEngine::drawPoints(const QPointF *points, int pointCount) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - d->paintEngine->drawPoints(points, pointCount); -} - -void QMacPrintEngine::drawEllipse(const QRectF &r) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - d->paintEngine->drawEllipse(r); -} - -void QMacPrintEngine::drawLines(const QLineF *lines, int lineCount) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - d->paintEngine->drawLines(lines, lineCount); -} - -void QMacPrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - d->paintEngine->drawPolygon(points, pointCount, mode); -} - -void QMacPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - d->paintEngine->drawPixmap(r, pm, sr); -} - -void QMacPrintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - d->paintEngine->drawImage(r, pm, sr, flags); -} - -void QMacPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - if (!d->embedFonts) - QPaintEngine::drawTextItem(p, ti); - else - d->paintEngine->drawTextItem(p, ti); -} - -void QMacPrintEngine::drawTiledPixmap(const QRectF &dr, const QPixmap &pixmap, const QPointF &sr) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - d->paintEngine->drawTiledPixmap(dr, pixmap, sr); -} - -void QMacPrintEngine::drawPath(const QPainterPath &path) -{ - Q_D(QMacPrintEngine); - Q_ASSERT(d->state == QPrinter::Active); - d->paintEngine->drawPath(path); -} - - -void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value) -{ - Q_D(QMacPrintEngine); - - d->valueCache.insert(key, value); - if (!d->printInfo) - return; - - switch (key) { - - // The following keys are properties or derived values and so cannot be set - case PPK_PageRect: - break; - case PPK_PaperRect: - break; - case PPK_PaperSources: - break; - case PPK_SupportsMultipleCopies: - break; - case PPK_SupportedResolutions: - break; - - // The following keys are settings that are unsupported by the Mac PrintEngine - case PPK_ColorMode: - break; - case PPK_CustomBase: - break; - case PPK_PageOrder: - // TODO Check if can be supported via Cups Options - break; - case PPK_PaperSource: - // TODO Check if can be supported via Cups Options - break; - case PPK_PrinterProgram: - break; - case PPK_SelectionOption: - break; - - // The following keys are properties and settings that are supported by the Mac PrintEngine - case PPK_FontEmbedding: - d->embedFonts = value.toBool(); - break; - case PPK_Resolution: { - int bestResolution = 0; - int dpi = value.toInt(); - int bestDistance = INT_MAX; - for (int resolution : d->m_printDevice->supportedResolutions()) { - if (dpi == resolution) { - bestResolution = resolution; - break; - } else { - int distance = qAbs(dpi - resolution); - if (distance < bestDistance) { - bestDistance = distance; - bestResolution = resolution; - } - } - } - PMResolution resolution; - resolution.hRes = resolution.vRes = bestResolution; - if (PMPrinterSetOutputResolution(d->m_printDevice->macPrinter(), d->settings(), &resolution) == noErr) { - // Setting the resolution succeeded. - // Now try to read the actual resolution selected by the OS. - if (PMPrinterGetOutputResolution(d->m_printDevice->macPrinter(), d->settings(), &d->resolution) != noErr) { - // Reading the resolution somehow failed; d->resolution is in undefined state. - // So use the value which was acceptable to PMPrinterSetOutputResolution. - d->resolution = resolution; - } - } - break; - } - case PPK_CollateCopies: - PMSetCollate(d->settings(), value.toBool()); - break; - case PPK_Creator: - d->m_creator = value.toString(); - break; - case PPK_DocumentName: - PMPrintSettingsSetJobName(d->settings(), QCFString(value.toString())); - break; - case PPK_Duplex: { - QPrint::DuplexMode mode = QPrint::DuplexMode(value.toInt()); - if (mode == property(PPK_Duplex).toInt() || !d->m_printDevice->supportedDuplexModes().contains(mode)) - break; - switch (mode) { - case QPrint::DuplexNone: - PMSetDuplex(d->settings(), kPMDuplexNone); - break; - case QPrint::DuplexAuto: - PMSetDuplex(d->settings(), d->m_pageLayout.orientation() == QPageLayout::Landscape ? kPMDuplexTumble : kPMDuplexNoTumble); - break; - case QPrint::DuplexLongSide: - PMSetDuplex(d->settings(), kPMDuplexNoTumble); - break; - case QPrint::DuplexShortSide: - PMSetDuplex(d->settings(), kPMDuplexTumble); - break; - default: - // Don't change - break; - } - break; - } - case PPK_FullPage: - if (value.toBool()) - d->m_pageLayout.setMode(QPageLayout::FullPageMode); - else - d->m_pageLayout.setMode(QPageLayout::StandardMode); - break; - case PPK_CopyCount: // fallthrough - case PPK_NumberOfCopies: - PMSetCopies(d->settings(), value.toInt(), false); - break; - case PPK_Orientation: { - // First try set the Mac format orientation, then set our orientation to match result - QPageLayout::Orientation newOrientation = QPageLayout::Orientation(value.toInt()); - PMOrientation macOrientation = (newOrientation == QPageLayout::Landscape) ? kPMLandscape : kPMPortrait; - PMSetOrientation(d->format(), macOrientation, kPMUnlocked); - PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean); - PMGetOrientation(d->format(), &macOrientation); - d->m_pageLayout.setOrientation(macOrientation == kPMLandscape ? QPageLayout::Landscape : QPageLayout::Portrait); - break; - } - case PPK_OutputFileName: - d->outputFilename = value.toString(); - break; - case PPK_PageSize: - d->setPageSize(QPageSize(QPageSize::PageSizeId(value.toInt()))); - break; - case PPK_PaperName: - // Get the named page size from the printer if supported - d->setPageSize(d->m_printDevice->supportedPageSize(value.toString())); - break; - case PPK_WindowsPageSize: - d->setPageSize(QPageSize(QPageSize::id(value.toInt()))); - break; - case PPK_PrinterName: { - QVariant pageSize = QVariant::fromValue(d->m_pageLayout.pageSize()); - const bool isFullPage = d->m_pageLayout.mode() == QPageLayout::FullPageMode; - QVariant orientation = QVariant::fromValue(d->m_pageLayout.orientation()); - QVariant margins = QVariant::fromValue(QPair(d->m_pageLayout.margins(), - d->m_pageLayout.units())); - QString id = value.toString(); - if (id.isEmpty()) - id = QCocoaPrinterSupport().defaultPrintDeviceId(); - else if (!QCocoaPrinterSupport().availablePrintDeviceIds().contains(id)) - break; - d->m_printDevice.reset(new QCocoaPrintDevice(id)); - PMPrinter printer = d->m_printDevice->macPrinter(); - PMRetain(printer); - PMSessionSetCurrentPMPrinter(d->session(), printer); - // Ensure the settings are up to date and valid - if (d->m_printDevice->supportedPageSize(pageSize.value()).isValid()) - setProperty(PPK_QPageSize, pageSize); - else - setProperty(PPK_CustomPaperSize, pageSize.value().size(QPageSize::Point)); - setProperty(PPK_FullPage, QVariant(isFullPage)); - setProperty(PPK_Orientation, orientation); - setProperty(PPK_QPageMargins, margins); - break; - } - case PPK_CustomPaperSize: - d->setPageSize(QPageSize(value.toSizeF(), QPageSize::Point)); - break; - case PPK_PageMargins: - { - QList margins(value.toList()); - Q_ASSERT(margins.size() == 4); - d->m_pageLayout.setMargins(QMarginsF(margins.at(0).toReal(), margins.at(1).toReal(), - margins.at(2).toReal(), margins.at(3).toReal())); - break; - } - case PPK_QPageSize: - d->setPageSize(value.value()); - break; - case PPK_QPageMargins: { - QPair pair = value.value >(); - d->m_pageLayout.setUnits(pair.second); - d->m_pageLayout.setMargins(pair.first); - break; - } - case PPK_QPageLayout: { - QPageLayout pageLayout = value.value(); - if (pageLayout.isValid() && d->m_printDevice->isValidPageLayout(pageLayout, d->resolution.hRes)) { - setProperty(PPK_QPageSize, QVariant::fromValue(pageLayout.pageSize())); - setProperty(PPK_FullPage, pageLayout.mode() == QPageLayout::FullPageMode); - setProperty(PPK_Orientation, QVariant::fromValue(pageLayout.orientation())); - d->m_pageLayout.setUnits(pageLayout.units()); - d->m_pageLayout.setMargins(pageLayout.margins()); - } - break; - } - // No default so that compiler will complain if new keys added and not handled in this engine - } -} - -QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const -{ - Q_D(const QMacPrintEngine); - QVariant ret; - - if (!d->printInfo && d->valueCache.contains(key)) - return *d->valueCache.find(key); - - switch (key) { - - // The following keys are settings that are unsupported by the Mac PrintEngine - // Return sensible default values to ensure consistent behavior across platforms - case PPK_ColorMode: - ret = QPrinter::Color; - break; - case PPK_CustomBase: - // Special case, leave null - break; - case PPK_PageOrder: - // TODO Check if can be supported via Cups Options - ret = QPrinter::FirstPageFirst; - break; - case PPK_PaperSource: - // TODO Check if can be supported via Cups Options - ret = QPrinter::Auto; - break; - case PPK_PaperSources: { - // TODO Check if can be supported via Cups Options - QList out; - out << int(QPrinter::Auto); - ret = out; - break; - } - case PPK_PrinterProgram: - ret = QString(); - break; - case PPK_SelectionOption: - ret = QString(); - break; - - // The following keys are properties and settings that are supported by the Mac PrintEngine - case PPK_FontEmbedding: - ret = d->embedFonts; - break; - case PPK_CollateCopies: { - Boolean status; - PMGetCollate(d->settings(), &status); - ret = bool(status); - break; - } - case PPK_Creator: - ret = d->m_creator; - break; - case PPK_DocumentName: { - CFStringRef name; - PMPrintSettingsGetJobName(d->settings(), &name); - ret = QString::fromCFString(name); - break; - } - case PPK_Duplex: { - PMDuplexMode mode = kPMDuplexNone; - PMGetDuplex(d->settings(), &mode); - switch (mode) { - case kPMDuplexNoTumble: - ret = QPrinter::DuplexLongSide; - break; - case kPMDuplexTumble: - ret = QPrinter::DuplexShortSide; - break; - case kPMDuplexNone: - default: - ret = QPrinter::DuplexNone; - break; - } - break; - } - case PPK_FullPage: - ret = d->m_pageLayout.mode() == QPageLayout::FullPageMode; - break; - case PPK_NumberOfCopies: - ret = 1; - break; - case PPK_CopyCount: { - UInt32 copies = 1; - PMGetCopies(d->settings(), &copies); - ret = (uint) copies; - break; - } - case PPK_SupportsMultipleCopies: - ret = true; - break; - case PPK_Orientation: - ret = d->m_pageLayout.orientation(); - break; - case PPK_OutputFileName: - ret = d->outputFilename; - break; - case PPK_PageRect: - // PageRect is returned in device pixels - ret = d->m_pageLayout.paintRectPixels(d->resolution.hRes); - break; - case PPK_PageSize: - ret = d->m_pageLayout.pageSize().id(); - break; - case PPK_PaperName: - ret = d->m_pageLayout.pageSize().name(); - break; - case PPK_WindowsPageSize: - ret = d->m_pageLayout.pageSize().windowsId(); - break; - case PPK_PaperRect: - // PaperRect is returned in device pixels - ret = d->m_pageLayout.fullRectPixels(d->resolution.hRes); - break; - case PPK_PrinterName: - return d->m_printDevice->id(); - break; - case PPK_Resolution: { - ret = d->resolution.hRes; - break; - } - case PPK_SupportedResolutions: { - QList list; - for (int resolution : d->m_printDevice->supportedResolutions()) - list << resolution; - ret = list; - break; - } - case PPK_CustomPaperSize: - ret = d->m_pageLayout.fullRectPoints().size(); - break; - case PPK_PageMargins: { - QList list; - QMarginsF margins = d->m_pageLayout.margins(QPageLayout::Point); - list << margins.left() << margins.top() << margins.right() << margins.bottom(); - ret = list; - break; - } - case PPK_QPageSize: - ret.setValue(d->m_pageLayout.pageSize()); - break; - case PPK_QPageMargins: { - QPair pair = qMakePair(d->m_pageLayout.margins(), d->m_pageLayout.units()); - ret.setValue(pair); - break; - } - case PPK_QPageLayout: - ret.setValue(d->m_pageLayout); - // No default so that compiler will complain if new keys added and not handled in this engine - } - return ret; -} - -QT_END_NAMESPACE - -#endif // QT_NO_PRINTER diff --git a/src/plugins/platforms/cocoa/qprintengine_mac_p.h b/src/plugins/platforms/cocoa/qprintengine_mac_p.h deleted file mode 100644 index 6a1ed2e263..0000000000 --- a/src/plugins/platforms/cocoa/qprintengine_mac_p.h +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPRINTENGINE_MAC_P_H -#define QPRINTENGINE_MAC_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 - -#ifndef QT_NO_PRINTER - -#include -#include -#include -#include - -#include "qcocoaprintdevice.h" - -#include "qpaintengine_mac_p.h" - -Q_FORWARD_DECLARE_OBJC_CLASS(NSPrintInfo); - -QT_BEGIN_NAMESPACE - -class QPrinterPrivate; -class QMacPrintEnginePrivate; -class QMacPrintEngine : public QPaintEngine, public QPrintEngine -{ - Q_DECLARE_PRIVATE(QMacPrintEngine) -public: - QMacPrintEngine(QPrinter::PrinterMode mode, const QString &deviceId); - - Qt::HANDLE handle() const; - - bool begin(QPaintDevice *dev); - bool end(); - virtual QPaintEngine::Type type() const { return QPaintEngine::MacPrinter; } - - QPaintEngine *paintEngine() const; - - void setProperty(PrintEnginePropertyKey key, const QVariant &value); - QVariant property(PrintEnginePropertyKey key) const; - - QPrinter::PrinterState printerState() const; - - bool newPage(); - bool abort(); - int metric(QPaintDevice::PaintDeviceMetric) const; - - //forwarded functions - - void updateState(const QPaintEngineState &state); - - virtual void drawLines(const QLineF *lines, int lineCount); - virtual void drawRects(const QRectF *r, int num); - virtual void drawPoints(const QPointF *p, int pointCount); - virtual void drawEllipse(const QRectF &r); - virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); - virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); - virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags); - virtual void drawTextItem(const QPointF &p, const QTextItem &ti); - virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); - virtual void drawPath(const QPainterPath &); - -private: - friend class QCocoaNativeInterface; -}; - -class QMacPrintEnginePrivate : public QPaintEnginePrivate -{ - Q_DECLARE_PUBLIC(QMacPrintEngine) -public: - QPrinter::PrinterMode mode; - QPrinter::PrinterState state; - QSharedPointer m_printDevice; - QPageLayout m_pageLayout; - NSPrintInfo *printInfo; - PMResolution resolution; - QString outputFilename; - QString m_creator; - QPaintEngine *paintEngine; - QHash valueCache; - uint embedFonts; - - QMacPrintEnginePrivate() : mode(QPrinter::ScreenResolution), state(QPrinter::Idle), - m_pageLayout(QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0, 0, 0, 0))), - printInfo(nullptr), paintEngine(nullptr), embedFonts(true) {} - ~QMacPrintEnginePrivate(); - - void initialize(); - void releaseSession(); - bool newPage_helper(); - void setPageSize(const QPageSize &pageSize); - inline bool isPrintSessionInitialized() const - { - return printInfo != 0; - } - - PMPageFormat format() const { return static_cast([printInfo PMPageFormat]); } - PMPrintSession session() const { return static_cast([printInfo PMPrintSession]); } - PMPrintSettings settings() const { return static_cast([printInfo PMPrintSettings]); } - - QPaintEngine *aggregateEngine() override { return paintEngine; } - Qt::HANDLE nativeHandle() override { return q_func()->handle(); } -}; - -QT_END_NAMESPACE - -#endif // QT_NO_PRINTER - -#endif // QPRINTENGINE_WIN_P_H diff --git a/src/plugins/printsupport/cocoa/main.cpp b/src/plugins/printsupport/cocoa/main.cpp index b6830c88d4..26ef5cca22 100644 --- a/src/plugins/printsupport/cocoa/main.cpp +++ b/src/plugins/printsupport/cocoa/main.cpp @@ -42,6 +42,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QCocoaPrinterSupportPlugin : public QPlatformPrinterSupportPlugin @@ -57,18 +59,8 @@ QPlatformPrinterSupport *QCocoaPrinterSupportPlugin::create(const QString &key) { if (key.compare(key, QLatin1String("cocoaprintersupport"), Qt::CaseInsensitive) != 0) return 0; - QGuiApplication *app = qobject_cast(QCoreApplication::instance()); - if (!app) - return 0; - QPlatformNativeInterface *platformNativeInterface = app->platformNativeInterface(); - int at = platformNativeInterface->metaObject()->indexOfMethod("createPlatformPrinterSupport()"); - if (at == -1) - return 0; - QMetaMethod createPlatformPrinterSupport = platformNativeInterface->metaObject()->method(at); - QPlatformPrinterSupport *platformPrinterSupport = 0; - if (!createPlatformPrinterSupport.invoke(platformNativeInterface, Q_RETURN_ARG(QPlatformPrinterSupport *, platformPrinterSupport))) - return 0; - return platformPrinterSupport; + + return new QCocoaPrinterSupport(); } QT_END_NAMESPACE diff --git a/src/printsupport/CMakeLists.txt b/src/printsupport/CMakeLists.txt index 9c73d78267..e032845cae 100644 --- a/src/printsupport/CMakeLists.txt +++ b/src/printsupport/CMakeLists.txt @@ -143,6 +143,17 @@ if(QT_FEATURE_printdialog) ) endif() +qt_extend_target(PrintSupport CONDITION MACOS AND TARGET Qt::Widgets + SOURCES + platform/macos/qpaintengine_mac.mm platform/macos/qpaintengine_mac_p.h + platform/macos/qcocoaprintdevice_p.h platform/macos/qcocoaprintdevice.mm + platform/macos/qcocoaprintersupport_p.h platform/macos/qcocoaprintersupport.mm + platform/macos/qprintengine_mac.mm platform/macos/qprintengine_mac_p.h + PUBLIC_LIBRARIES + Qt::WidgetsPrivate + Cups::Cups +) + qt_extend_target(PrintSupport CONDITION MACOS AND QT_FEATURE_printdialog SOURCES dialogs/qpagesetupdialog_mac.mm diff --git a/src/printsupport/dialogs/qpagesetupdialog_mac.mm b/src/printsupport/dialogs/qpagesetupdialog_mac.mm index a3511fe7b6..b88d03696a 100644 --- a/src/printsupport/dialogs/qpagesetupdialog_mac.mm +++ b/src/printsupport/dialogs/qpagesetupdialog_mac.mm @@ -47,6 +47,8 @@ #include #include +#include + QT_USE_NAMESPACE @class QT_MANGLE_NAMESPACE(QCocoaPageLayoutDelegate); @@ -114,13 +116,7 @@ void QMacPageSetupDialogPrivate::openCocoaPageLayout(Qt::WindowModality modality { Q_Q(QPageSetupDialog); - // get the NSPrintInfo from the print engine in the platform plugin - void *voidp = 0; - (void) QMetaObject::invokeMethod(qApp->platformNativeInterface(), - "NSPrintInfoForPrintEngine", - Q_RETURN_ARG(void *, voidp), - Q_ARG(QPrintEngine *, printer->printEngine())); - printInfo = static_cast(voidp); + printInfo = static_cast(printer->printEngine())->printInfo(); [printInfo retain]; pageLayout = [NSPageLayout pageLayout]; diff --git a/src/printsupport/dialogs/qprintdialog_mac.mm b/src/printsupport/dialogs/qprintdialog_mac.mm index a4101f7ec0..9f84f98588 100644 --- a/src/printsupport/dialogs/qprintdialog_mac.mm +++ b/src/printsupport/dialogs/qprintdialog_mac.mm @@ -49,6 +49,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE extern qreal qt_pointMultiplier(QPageLayout::Unit unit); @@ -216,13 +218,7 @@ void QPrintDialogPrivate::openCocoaPrintPanel(Qt::WindowModality modality) Q_Q(QPrintDialog); if (printer->outputFormat() == QPrinter::NativeFormat) { - // get the NSPrintInfo from the print engine in the platform plugin - void *voidp = 0; - (void) QMetaObject::invokeMethod(qApp->platformNativeInterface(), - "NSPrintInfoForPrintEngine", - Q_RETURN_ARG(void *, voidp), - Q_ARG(QPrintEngine *, printer->printEngine())); - printInfo = static_cast(voidp); + printInfo = static_cast(printer->printEngine())->printInfo(); [printInfo retain]; } else { printInfo = [NSPrintInfo.sharedPrintInfo retain]; diff --git a/src/printsupport/platform/macos/macos.pri b/src/printsupport/platform/macos/macos.pri new file mode 100644 index 0000000000..fc5c5c9b93 --- /dev/null +++ b/src/printsupport/platform/macos/macos.pri @@ -0,0 +1,13 @@ +SOURCES += \ + $$PWD/qprintengine_mac.mm \ + $$PWD/qpaintengine_mac.mm \ + $$PWD/qcocoaprintersupport.mm \ + $$PWD/qcocoaprintdevice.mm + +HEADERS += \ + $$PWD/qcocoaprintersupport_p.h \ + $$PWD/qcocoaprintdevice_p.h \ + $$PWD/qprintengine_mac_p.h \ + $$PWD/qpaintengine_mac_p.h + +LIBS += -framework ApplicationServices -lcups diff --git a/src/printsupport/platform/macos/qcocoaprintdevice.mm b/src/printsupport/platform/macos/qcocoaprintdevice.mm new file mode 100644 index 0000000000..118adc0b96 --- /dev/null +++ b/src/printsupport/platform/macos/qcocoaprintdevice.mm @@ -0,0 +1,498 @@ +/**************************************************************************** +** +** Copyright (C) 2014 John Layt +** 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 + +#include "qcocoaprintdevice_p.h" + +#if QT_CONFIG(mimetype) +#include +#endif +#include + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PRINTER + +// The CUPS PPD APIs were deprecated in CUPS 1.6/macOS 10.8, but +// as long as we're supporting RHEL 6, which still ships CUPS 1.4 +// we're not going to rewrite this, as we want to share the code +// between macOS and Linux for the CUPS-bits. See discussion in +// https://bugreports.qt.io/browse/QTBUG-56545 +#pragma message "Disabling CUPS PPD deprecation warnings. This should be fixed once we drop support for RHEL6 (QTBUG-56545)" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +static QPrint::DuplexMode macToDuplexMode(const PMDuplexMode &mode) +{ + if (mode == kPMDuplexTumble) + return QPrint::DuplexShortSide; + else if (mode == kPMDuplexNoTumble) + return QPrint::DuplexLongSide; + else // kPMDuplexNone or kPMSimplexTumble + return QPrint::DuplexNone; +} + +QCocoaPrintDevice::QCocoaPrintDevice() + : QPlatformPrintDevice(), + m_printer(nullptr), + m_session(nullptr), + m_ppd(nullptr) +{ +} + +QCocoaPrintDevice::QCocoaPrintDevice(const QString &id) + : QPlatformPrintDevice(id), + m_printer(nullptr), + m_session(nullptr), + m_ppd(nullptr) +{ + if (!id.isEmpty()) { + m_printer = PMPrinterCreateFromPrinterID(id.toCFString()); + if (m_printer) { + m_name = QString::fromCFString(PMPrinterGetName(m_printer)); + m_location = QString::fromCFString(PMPrinterGetLocation(m_printer)); + CFStringRef cfMakeAndModel; + if (PMPrinterGetMakeAndModelName(m_printer, &cfMakeAndModel) == noErr) + m_makeAndModel = QString::fromCFString(cfMakeAndModel); + Boolean isRemote; + if (PMPrinterIsRemote(m_printer, &isRemote) == noErr) + m_isRemote = isRemote; + if (PMCreateSession(&m_session) == noErr) + PMSessionSetCurrentPMPrinter(m_session, m_printer); + + // No native api to query these options, need to use PPD directly, note is deprecated from 1.6 onwards + if (openPpdFile()) { + // Note this is if the hardware does multiple copies, not if Cups can + m_supportsMultipleCopies = !m_ppd->manual_copies; + // Note this is if the hardware does collation, not if Cups can + ppd_option_t *collate = ppdFindOption(m_ppd, "Collate"); + if (collate) + m_supportsCollateCopies = true; + m_supportsCustomPageSizes = m_ppd->custom_max[0] > 0 && m_ppd->custom_max[1] > 0; + m_minimumPhysicalPageSize = QSize(m_ppd->custom_min[0], m_ppd->custom_min[1]); + m_maximumPhysicalPageSize = QSize(m_ppd->custom_max[0], m_ppd->custom_max[1]); + m_customMargins = QMarginsF(m_ppd->custom_margins[0], m_ppd->custom_margins[3], + m_ppd->custom_margins[2], m_ppd->custom_margins[1]); + } + } + } +} + +QCocoaPrintDevice::~QCocoaPrintDevice() +{ + if (m_ppd) + ppdClose(m_ppd); + for (PMPaper paper : m_macPapers) + PMRelease(paper); + // Releasing the session appears to also release the printer + if (m_session) + PMRelease(m_session); + else if (m_printer) + PMRelease(m_printer); +} + +bool QCocoaPrintDevice::isValid() const +{ + return m_printer ? true : false; +} + +bool QCocoaPrintDevice::isDefault() const +{ + return PMPrinterIsDefault(m_printer); +} + +QPrint::DeviceState QCocoaPrintDevice::state() const +{ + PMPrinterState state; + if (PMPrinterGetState(m_printer, &state) == noErr) { + if (state == kPMPrinterIdle) + return QPrint::Idle; + else if (state == kPMPrinterProcessing) + return QPrint::Active; + else if (state == kPMPrinterStopped) + return QPrint::Error; + } + return QPrint::Error; +} + +QPageSize QCocoaPrintDevice::createPageSize(const PMPaper &paper) const +{ + CFStringRef key; + double width; + double height; + CFStringRef localizedName; + if (PMPaperGetPPDPaperName(paper, &key) == noErr + && PMPaperGetWidth(paper, &width) == noErr + && PMPaperGetHeight(paper, &height) == noErr + && PMPaperCreateLocalizedName(paper, m_printer, &localizedName) == noErr) { + QPageSize pageSize = QPlatformPrintDevice::createPageSize(QString::fromCFString(key),QSize(width, height), + QString::fromCFString(localizedName)); + CFRelease(localizedName); + return pageSize; + } + return QPageSize(); +} + +void QCocoaPrintDevice::loadPageSizes() const +{ + m_pageSizes.clear(); + for (PMPaper paper : m_macPapers) + PMRelease(paper); + m_macPapers.clear(); + m_printableMargins.clear(); + CFArrayRef paperSizes; + if (PMPrinterGetPaperList(m_printer, &paperSizes) == noErr) { + int count = CFArrayGetCount(paperSizes); + for (int i = 0; i < count; ++i) { + PMPaper paper = static_cast(const_cast(CFArrayGetValueAtIndex(paperSizes, i))); + QPageSize pageSize = createPageSize(paper); + if (pageSize.isValid()) { + m_pageSizes.append(pageSize); + PMRetain(paper); + m_macPapers.insert(pageSize.key(), paper); + PMPaperMargins printMargins; + PMPaperGetMargins(paper, &printMargins); + m_printableMargins.insert(pageSize.key(), QMarginsF(printMargins.left, printMargins.top, + printMargins.right, printMargins.bottom)); + } + } + } + m_havePageSizes = true; +} + +QPageSize QCocoaPrintDevice::defaultPageSize() const +{ + QPageSize pageSize; + PMPageFormat pageFormat; + PMPaper paper; + if (PMCreatePageFormat(&pageFormat) == noErr) { + if (PMSessionDefaultPageFormat(m_session, pageFormat) == noErr + && PMGetPageFormatPaper(pageFormat, &paper) == noErr) { + pageSize = createPageSize(paper); + } + PMRelease(pageFormat); + } + return pageSize; +} + +QMarginsF QCocoaPrintDevice::printableMargins(const QPageSize &pageSize, + QPageLayout::Orientation orientation, + int resolution) const +{ + Q_UNUSED(orientation) + Q_UNUSED(resolution) + if (!m_havePageSizes) + loadPageSizes(); + if (m_printableMargins.contains(pageSize.key())) + return m_printableMargins.value(pageSize.key()); + return m_customMargins; +} + +void QCocoaPrintDevice::loadResolutions() const +{ + m_resolutions.clear(); + UInt32 count; + if (PMPrinterGetPrinterResolutionCount(m_printer, &count) == noErr) { + // 1-based index + for (UInt32 i = 1; i <= count; ++i) { + PMResolution resolution; + if (PMPrinterGetIndexedPrinterResolution(m_printer, i, &resolution) == noErr) + m_resolutions.append(int(resolution.hRes)); + } + } + m_haveResolutions = true; +} + +int QCocoaPrintDevice::defaultResolution() const +{ + int defaultResolution = 72; + PMPrintSettings settings; + if (PMCreatePrintSettings(&settings) == noErr) { + PMResolution resolution; + if (PMSessionDefaultPrintSettings(m_session, settings) == noErr + && PMPrinterGetOutputResolution(m_printer, settings, &resolution) == noErr) { + // PMPrinterGetOutputResolution usually fails with -9589 kPMKeyNotFound as not set in PPD + defaultResolution = int(resolution.hRes); + } + PMRelease(settings); + } + // If no value returned (usually means not set in PPD) then use supported resolutions which + // OSX will have populated with at least one default value (but why not returned by call?) + if (defaultResolution <= 0) { + if (!m_haveResolutions) + loadResolutions(); + if (m_resolutions.count() > 0) + return m_resolutions.at(0); // First value or highest? Only likely to be one anyway. + return 72; // TDOD More sensible default value??? + } + return defaultResolution; +} + +void QCocoaPrintDevice::loadInputSlots() const +{ + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // TODO Deal with concatenated names like Tray1Manual or Tray1_Man, + // will currently show as CustomInputSlot + // TODO Deal with separate ManualFeed key + // Try load standard PPD options first + m_inputSlots.clear(); + if (m_ppd) { + ppd_option_t *inputSlots = ppdFindOption(m_ppd, "InputSlot"); + if (inputSlots) { + for (int i = 0; i < inputSlots->num_choices; ++i) + m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[i])); + } + // If no result, try just the default + if (m_inputSlots.size() == 0) { + inputSlots = ppdFindOption(m_ppd, "DefaultInputSlot"); + if (inputSlots) + m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[0])); + } + } + // If still no result, just use Auto + if (m_inputSlots.size() == 0) + m_inputSlots.append(QPlatformPrintDevice::defaultInputSlot()); + m_haveInputSlots = true; +} + +QPrint::InputSlot QCocoaPrintDevice::defaultInputSlot() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Try load standard PPD option first + if (m_ppd) { + ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultInputSlot"); + if (inputSlot) + return QPrintUtils::ppdChoiceToInputSlot(inputSlot->choices[0]); + // If no result, then try a marked option + ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "InputSlot"); + if (defaultChoice) + return QPrintUtils::ppdChoiceToInputSlot(*defaultChoice); + } + // Otherwise return Auto + return QPlatformPrintDevice::defaultInputSlot(); +} + +void QCocoaPrintDevice::loadOutputBins() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + m_outputBins.clear(); + if (m_ppd) { + ppd_option_t *outputBins = ppdFindOption(m_ppd, "OutputBin"); + if (outputBins) { + for (int i = 0; i < outputBins->num_choices; ++i) + m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[i])); + } + // If no result, try just the default + if (m_outputBins.size() == 0) { + outputBins = ppdFindOption(m_ppd, "DefaultOutputBin"); + if (outputBins) + m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[0])); + } + } + // If still no result, just use Auto + if (m_outputBins.size() == 0) + m_outputBins.append(QPlatformPrintDevice::defaultOutputBin()); + m_haveOutputBins = true; +} + +QPrint::OutputBin QCocoaPrintDevice::defaultOutputBin() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Try load standard PPD option first + if (m_ppd) { + ppd_option_t *outputBin = ppdFindOption(m_ppd, "DefaultOutputBin"); + if (outputBin) + return QPrintUtils::ppdChoiceToOutputBin(outputBin->choices[0]); + // If no result, then try a marked option + ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "OutputBin"); + if (defaultChoice) + return QPrintUtils::ppdChoiceToOutputBin(*defaultChoice); + } + // Otherwise return AutoBin + return QPlatformPrintDevice::defaultOutputBin(); +} + +void QCocoaPrintDevice::loadDuplexModes() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Try load standard PPD options first + m_duplexModes.clear(); + if (m_ppd) { + ppd_option_t *duplexModes = ppdFindOption(m_ppd, "Duplex"); + if (duplexModes) { + for (int i = 0; i < duplexModes->num_choices; ++i) + m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[i].choice)); + } + // If no result, try just the default + if (m_duplexModes.size() == 0) { + duplexModes = ppdFindOption(m_ppd, "DefaultDuplex"); + if (duplexModes) + m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[0].choice)); + } + } + // If still no result, or not added in PPD, then add None + if (m_duplexModes.size() == 0 || !m_duplexModes.contains(QPrint::DuplexNone)) + m_duplexModes.append(QPrint::DuplexNone); + // If have both modes, then can support DuplexAuto + if (m_duplexModes.contains(QPrint::DuplexLongSide) && m_duplexModes.contains(QPrint::DuplexShortSide)) + m_duplexModes.append(QPrint::DuplexAuto); + m_haveDuplexModes = true; +} + +QPrint::DuplexMode QCocoaPrintDevice::defaultDuplexMode() const +{ + QPrint::DuplexMode defaultMode = QPrint::DuplexNone; + PMPrintSettings settings; + if (PMCreatePrintSettings(&settings) == noErr) { + PMDuplexMode duplexMode; + if (PMSessionDefaultPrintSettings(m_session, settings) == noErr + && PMGetDuplex(settings, &duplexMode) == noErr) { + defaultMode = macToDuplexMode(duplexMode); + } + PMRelease(settings); + } + return defaultMode; +} + +void QCocoaPrintDevice::loadColorModes() const +{ + // No native api to query, use PPD directly + m_colorModes.clear(); + m_colorModes.append(QPrint::GrayScale); + if (!m_ppd || (m_ppd && m_ppd->color_device)) + m_colorModes.append(QPrint::Color); + m_haveColorModes = true; +} + +QPrint::ColorMode QCocoaPrintDevice::defaultColorMode() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Not a proper option, usually only know if supports color or not, but some + // users known to abuse ColorModel to always force GrayScale. + if (m_ppd && supportedColorModes().contains(QPrint::Color)) { + ppd_option_t *colorModel = ppdFindOption(m_ppd, "DefaultColorModel"); + if (!colorModel) + colorModel = ppdFindOption(m_ppd, "ColorModel"); + if (!colorModel || qstrcmp(colorModel->defchoice, "Gray") != 0) + return QPrint::Color; + } + return QPrint::GrayScale; +} + +#if QT_CONFIG(mimetype) +void QCocoaPrintDevice::loadMimeTypes() const +{ + // TODO Check how settings affect returned list + m_mimeTypes.clear(); + QMimeDatabase db; + PMPrintSettings settings; + if (PMCreatePrintSettings(&settings) == noErr) { + CFArrayRef mimeTypes; + if (PMPrinterGetMimeTypes(m_printer, settings, &mimeTypes) == noErr) { + int count = CFArrayGetCount(mimeTypes); + for (int i = 0; i < count; ++i) { + CFStringRef mimeName = static_cast(const_cast(CFArrayGetValueAtIndex(mimeTypes, i))); + QMimeType mimeType = db.mimeTypeForName(QString::fromCFString(mimeName)); + if (mimeType.isValid()) + m_mimeTypes.append(mimeType); + } + } + PMRelease(settings); + } + m_haveMimeTypes = true; +} +#endif // mimetype + +bool QCocoaPrintDevice::openPpdFile() +{ + if (m_ppd) + ppdClose(m_ppd); + m_ppd = nullptr; + CFURLRef ppdURL = nullptr; + char ppdPath[MAXPATHLEN]; + if (PMPrinterCopyDescriptionURL(m_printer, kPMPPDDescriptionType, &ppdURL) == noErr + && ppdURL) { + if (CFURLGetFileSystemRepresentation(ppdURL, true, (UInt8*)ppdPath, sizeof(ppdPath))) + m_ppd = ppdOpenFile(ppdPath); + CFRelease(ppdURL); + } + return m_ppd ? true : false; +} + +PMPrinter QCocoaPrintDevice::macPrinter() const +{ + return m_printer; +} + +// Returns a cached printer PMPaper, or creates and caches a new custom PMPaper +// Caller should never release a cached PMPaper! +PMPaper QCocoaPrintDevice::macPaper(const QPageSize &pageSize) const +{ + if (!m_havePageSizes) + loadPageSizes(); + // If keys match, then is a supported size or an existing custom size + if (m_macPapers.contains(pageSize.key())) + return m_macPapers.value(pageSize.key()); + // For any other page size, whether custom or just unsupported, needs to be a custom PMPaper + PMPaper paper = nullptr; + PMPaperMargins paperMargins; + paperMargins.left = m_customMargins.left(); + paperMargins.right = m_customMargins.right(); + paperMargins.top = m_customMargins.top(); + paperMargins.bottom = m_customMargins.bottom(); + PMPaperCreateCustom(m_printer, QCFString(pageSize.key()), QCFString(pageSize.name()), + pageSize.sizePoints().width(), pageSize.sizePoints().height(), + &paperMargins, &paper); + m_macPapers.insert(pageSize.key(), paper); + return paper; +} + +#pragma clang diagnostic pop + +#endif // QT_NO_PRINTER + +QT_END_NAMESPACE diff --git a/src/printsupport/platform/macos/qcocoaprintdevice_p.h b/src/printsupport/platform/macos/qcocoaprintdevice_p.h new file mode 100644 index 0000000000..f40a3b2666 --- /dev/null +++ b/src/printsupport/platform/macos/qcocoaprintdevice_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2014 John Layt +** 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 QCOCOAPRINTDEVICE_H +#define QCOCOAPRINTDEVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of internal files. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include + +#ifndef QT_NO_PRINTER + +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QCocoaPrintDevice : public QPlatformPrintDevice +{ +public: + QCocoaPrintDevice(); + explicit QCocoaPrintDevice(const QString &id); + virtual ~QCocoaPrintDevice(); + + bool isValid() const override; + bool isDefault() const override; + + QPrint::DeviceState state() const override; + + QPageSize defaultPageSize() const override; + + QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation, + int resolution) const override; + + int defaultResolution() const override; + + QPrint::InputSlot defaultInputSlot() const override; + + QPrint::OutputBin defaultOutputBin() const override; + + QPrint::DuplexMode defaultDuplexMode() const override; + + QPrint::ColorMode defaultColorMode() const override; + + PMPrinter macPrinter() const; + PMPaper macPaper(const QPageSize &pageSize) const; + +protected: + void loadPageSizes() const override; + void loadResolutions() const override; + void loadInputSlots() const override; + void loadOutputBins() const override; + void loadDuplexModes() const override; + void loadColorModes() const override; +#if QT_CONFIG(mimetype) + void loadMimeTypes() const override; +#endif + +private: + QPageSize createPageSize(const PMPaper &paper) const; + bool openPpdFile(); + + // Mac Core Printing + PMPrinter m_printer; + PMPrintSession m_session; + mutable QHash m_macPapers; + + // PPD File + ppd_file_t *m_ppd; + + QMarginsF m_customMargins; + mutable QHash m_printableMargins; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER +#endif // QCOCOAPRINTDEVICE_H diff --git a/src/printsupport/platform/macos/qcocoaprintersupport.mm b/src/printsupport/platform/macos/qcocoaprintersupport.mm new file mode 100644 index 0000000000..0a285dddc6 --- /dev/null +++ b/src/printsupport/platform/macos/qcocoaprintersupport.mm @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPrintSupport 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 + +#include "qcocoaprintersupport_p.h" + +#ifndef QT_NO_PRINTER + +#include + +#include + +#include "qcocoaprintdevice_p.h" +#include "qprintengine_mac_p.h" + +#include + +QT_BEGIN_NAMESPACE + +QCocoaPrinterSupport::QCocoaPrinterSupport() +{ } + +QCocoaPrinterSupport::~QCocoaPrinterSupport() +{ } + +QPrintEngine *QCocoaPrinterSupport::createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId) +{ + return new QMacPrintEngine(printerMode, deviceId); +} + +QPaintEngine *QCocoaPrinterSupport::createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode printerMode) +{ + Q_UNUSED(printerMode); + /* + QMacPrintEngine multiply inherits from QPrintEngine and QPaintEngine, + the cast here allows conversion of QMacPrintEngine* to QPaintEngine* + */ + return static_cast(printEngine); +} + +QPrintDevice QCocoaPrinterSupport::createPrintDevice(const QString &id) +{ + return QPlatformPrinterSupport::createPrintDevice(new QCocoaPrintDevice(id)); +} + +QStringList QCocoaPrinterSupport::availablePrintDeviceIds() const +{ + QStringList list; + QCFType printerList; + if (PMServerCreatePrinterList(kPMServerLocal, &printerList) == noErr) { + CFIndex count = CFArrayGetCount(printerList); + for (CFIndex i = 0; i < count; ++i) { + PMPrinter printer = static_cast(const_cast(CFArrayGetValueAtIndex(printerList, i))); + list.append(QString::fromCFString(PMPrinterGetID(printer))); + } + } + return list; +} + +QString QCocoaPrinterSupport::defaultPrintDeviceId() const +{ + QCFType printerList; + if (PMServerCreatePrinterList(kPMServerLocal, &printerList) == noErr) { + CFIndex count = CFArrayGetCount(printerList); + for (CFIndex i = 0; i < count; ++i) { + PMPrinter printer = static_cast(const_cast(CFArrayGetValueAtIndex(printerList, i))); + if (PMPrinterIsDefault(printer)) + return QString::fromCFString(PMPrinterGetID(printer)); + } + } + return QString(); +} + +QT_END_NAMESPACE + +#endif //QT_NO_PRINTER diff --git a/src/printsupport/platform/macos/qcocoaprintersupport_p.h b/src/printsupport/platform/macos/qcocoaprintersupport_p.h new file mode 100644 index 0000000000..c12e8c355b --- /dev/null +++ b/src/printsupport/platform/macos/qcocoaprintersupport_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPrintSupport 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 QCOCOAPRINTERSUPPORT_H +#define QCOCOAPRINTERSUPPORT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of internal files. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include +#ifndef QT_NO_PRINTER + +#include + +QT_BEGIN_NAMESPACE + +class Q_PRINTSUPPORT_EXPORT QCocoaPrinterSupport : public QPlatformPrinterSupport +{ +public: + QCocoaPrinterSupport(); + ~QCocoaPrinterSupport(); + + QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) override; + QPaintEngine *createPaintEngine(QPrintEngine *, QPrinter::PrinterMode printerMode) override; + + QPrintDevice createPrintDevice(const QString &id) override; + QStringList availablePrintDeviceIds() const override; + QString defaultPrintDeviceId() const override; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER +#endif // QCOCOAPRINTERSUPPORT_H diff --git a/src/printsupport/platform/macos/qpaintengine_mac.mm b/src/printsupport/platform/macos/qpaintengine_mac.mm new file mode 100644 index 0000000000..fd82539df6 --- /dev/null +++ b/src/printsupport/platform/macos/qpaintengine_mac.mm @@ -0,0 +1,1422 @@ +/**************************************************************************** +** +** 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 +#include + +#include "qpaintengine_mac_p.h" +#include "qprintengine_mac_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/***************************************************************************** + QCoreGraphicsPaintEngine utility functions + *****************************************************************************/ + +void qt_mac_cgimage_data_free(void *, const void *memoryToFree, size_t) +{ + free(const_cast(memoryToFree)); +} + +CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr) +{ + QImage image = pixmap.toImage(); + if (image.format() != QImage::Format_ARGB32_Premultiplied) + image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + + const int sx = qRound(sr.x()), sy = qRound(sr.y()), sw = qRound(sr.width()), sh = qRound(sr.height()); + const qsizetype sbpr = image.bytesPerLine(); + const uint nbytes = sw * sh; + // alpha is always 255 for bitmaps, ignore it in this case. + const quint32 mask = pixmap.depth() == 1 ? 0x00ffffff : 0xffffffff; + quint8 *dptr = static_cast(malloc(nbytes)); + quint32 *sptr = reinterpret_cast(image.scanLine(0)), *srow; + for (int y = sy, offset=0; y < sh; ++y) { + srow = sptr + (y * (sbpr / 4)); + for (int x = sx; x < sw; ++x) + *(dptr+(offset++)) = (*(srow+x) & mask) ? 255 : 0; + } + QCFType provider = CGDataProviderCreateWithData(nullptr, dptr, nbytes, qt_mac_cgimage_data_free); + return CGImageMaskCreate(sw, sh, 8, 8, nbytes / sh, provider, nullptr, false); +} + +//conversion +inline static float qt_mac_convert_color_to_cg(int c) { return ((float)c * 1000 / 255) / 1000; } +CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &t) { + return CGAffineTransformMake(t.m11(), t.m12(), t.m21(), t.m22(), t.dx(), t.dy()); +} + +inline static QCFType cgColorForQColor(const QColor &col) +{ + CGFloat components[] = { + qt_mac_convert_color_to_cg(col.red()), + qt_mac_convert_color_to_cg(col.green()), + qt_mac_convert_color_to_cg(col.blue()), + qt_mac_convert_color_to_cg(col.alpha()) + }; + QCFType colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + return CGColorCreate(colorSpace, components); +} + +// There's architectural problems with using native gradients +// on the Mac at the moment, so disable them. +// #define QT_MAC_USE_NATIVE_GRADIENTS + +#ifdef QT_MAC_USE_NATIVE_GRADIENTS +static bool drawGradientNatively(const QGradient *gradient) +{ + return gradient->spread() == QGradient::PadSpread; +} + +// gradiant callback +static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out) +{ + QBrush *brush = static_cast(info); + Q_ASSERT(brush && brush->gradient()); + + const QGradientStops stops = brush->gradient()->stops(); + const int n = stops.count(); + Q_ASSERT(n >= 1); + const QGradientStop *begin = stops.constBegin(); + const QGradientStop *end = begin + n; + + qreal p = in[0]; + const QGradientStop *i = begin; + while (i != end && i->first < p) + ++i; + + QRgb c; + if (i == begin) { + c = begin->second.rgba(); + } else if (i == end) { + c = (end - 1)->second.rgba(); + } else { + const QGradientStop &s1 = *(i - 1); + const QGradientStop &s2 = *i; + qreal p1 = s1.first; + qreal p2 = s2.first; + QRgb c1 = s1.second.rgba(); + QRgb c2 = s2.second.rgba(); + int idist = 256 * (p - p1) / (p2 - p1); + int dist = 256 - idist; + c = qRgba(INTERPOLATE_PIXEL_256(qRed(c1), dist, qRed(c2), idist), + INTERPOLATE_PIXEL_256(qGreen(c1), dist, qGreen(c2), idist), + INTERPOLATE_PIXEL_256(qBlue(c1), dist, qBlue(c2), idist), + INTERPOLATE_PIXEL_256(qAlpha(c1), dist, qAlpha(c2), idist)); + } + + out[0] = qt_mac_convert_color_to_cg(qRed(c)); + out[1] = qt_mac_convert_color_to_cg(qGreen(c)); + out[2] = qt_mac_convert_color_to_cg(qBlue(c)); + out[3] = qt_mac_convert_color_to_cg(qAlpha(c)); +} +#endif + +//clipping handling +void QCoreGraphicsPaintEnginePrivate::resetClip() +{ + static bool inReset = false; + if (inReset) + return; + inReset = true; + + CGAffineTransform old_xform = CGContextGetCTM(hd); + + //setup xforms + CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform)); + while (stackCount > 0) { + restoreGraphicsState(); + } + saveGraphicsState(); + inReset = false; + //reset xforms + CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd))); + CGContextConcatCTM(hd, old_xform); +} + +static CGRect qt_mac_compose_rect(const QRectF &r, float off=0) +{ + return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height()); +} + +static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0) +{ + CGMutablePathRef ret = CGPathCreateMutable(); + QPointF startPt; + for (int i=0; i 0 + && p.elementAt(i - 1).x == startPt.x() + && p.elementAt(i - 1).y == startPt.y()) + CGPathCloseSubpath(ret); + startPt = QPointF(elm.x, elm.y); + CGPathMoveToPoint(ret, 0, elm.x+off, elm.y+off); + break; + case QPainterPath::LineToElement: + CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off); + break; + case QPainterPath::CurveToElement: + Q_ASSERT(p.elementAt(i+1).type == QPainterPath::CurveToDataElement); + Q_ASSERT(p.elementAt(i+2).type == QPainterPath::CurveToDataElement); + CGPathAddCurveToPoint(ret, 0, + elm.x+off, elm.y+off, + p.elementAt(i+1).x+off, p.elementAt(i+1).y+off, + p.elementAt(i+2).x+off, p.elementAt(i+2).y+off); + i+=2; + break; + default: + qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type); + break; + } + } + if (!p.isEmpty() + && p.elementAt(p.elementCount() - 1).x == startPt.x() + && p.elementAt(p.elementCount() - 1).y == startPt.y()) + CGPathCloseSubpath(ret); + return ret; +} + +//pattern handling (tiling) +#if 1 +# define QMACPATTERN_MASK_MULTIPLIER 32 +#else +# define QMACPATTERN_MASK_MULTIPLIER 1 +#endif +class QMacPattern +{ +public: + QMacPattern() : as_mask(false), pdev(0), image(0) { data.bytes = 0; } + ~QMacPattern() { CGImageRelease(image); } + int width() { + if (image) + return CGImageGetWidth(image); + if (data.bytes) + return 8*QMACPATTERN_MASK_MULTIPLIER; + return data.pixmap.width(); + } + int height() { + if (image) + return CGImageGetHeight(image); + if (data.bytes) + return 8*QMACPATTERN_MASK_MULTIPLIER; + return data.pixmap.height(); + } + + //input + QColor foreground; + bool as_mask; + struct { + QPixmap pixmap; + const uchar *bytes; + } data; + QPaintDevice *pdev; + //output + CGImageRef image; +}; +static void qt_mac_draw_pattern(void *info, CGContextRef c) +{ + QMacPattern *pat = (QMacPattern*)info; + int w = 0, h = 0; + bool isBitmap = (pat->data.pixmap.depth() == 1); + if (!pat->image) { //lazy cache + if (pat->as_mask) { + Q_ASSERT(pat->data.bytes); + w = h = 8; +#if (QMACPATTERN_MASK_MULTIPLIER == 1) + CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, pat->data.bytes, w*h, nullptr); + pat->image = CGImageMaskCreate(w, h, 1, 1, 1, provider, nullptr, false); + CGDataProviderRelease(provider); +#else + const int numBytes = (w*h)/sizeof(uchar); + uchar xor_bytes[numBytes]; + for (int i = 0; i < numBytes; ++i) + xor_bytes[i] = pat->data.bytes[i] ^ 0xFF; + CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, xor_bytes, w*h, nullptr); + CGImageRef swatch = CGImageMaskCreate(w, h, 1, 1, 1, provider, nullptr, false); + CGDataProviderRelease(provider); + + const QColor c0(0, 0, 0, 0), c1(255, 255, 255, 255); + QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER); + pm.fill(c0); + QMacCGContext pm_ctx(&pm); + CGContextSetFillColorWithColor(c, cgColorForQColor(c1)); + CGRect rect = CGRectMake(0, 0, w, h); + for (int x = 0; x < QMACPATTERN_MASK_MULTIPLIER; ++x) { + rect.origin.x = x * w; + for (int y = 0; y < QMACPATTERN_MASK_MULTIPLIER; ++y) { + rect.origin.y = y * h; + qt_mac_drawCGImage(pm_ctx, &rect, swatch); + } + } + pat->image = qt_mac_create_imagemask(pm, pm.rect()); + CGImageRelease(swatch); + w *= QMACPATTERN_MASK_MULTIPLIER; + h *= QMACPATTERN_MASK_MULTIPLIER; +#endif + } else { + w = pat->data.pixmap.width(); + h = pat->data.pixmap.height(); + if (isBitmap) + pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect()); + else + pat->image = qt_mac_toCGImage(pat->data.pixmap.toImage()); + } + } else { + w = CGImageGetWidth(pat->image); + h = CGImageGetHeight(pat->image); + } + + //draw + bool needRestore = false; + if (CGImageIsMask(pat->image)) { + CGContextSaveGState(c); + CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground)); + } + CGRect rect = CGRectMake(0, 0, w, h); + qt_mac_drawCGImage(c, &rect, pat->image); + if (needRestore) + CGContextRestoreGState(c); +} +static void qt_mac_dispose_pattern(void *info) +{ + QMacPattern *pat = (QMacPattern*)info; + delete pat; +} + +/***************************************************************************** + QCoreGraphicsPaintEngine member functions + *****************************************************************************/ + +inline static QPaintEngine::PaintEngineFeatures qt_mac_cg_features() +{ + return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent + & ~QPaintEngine::PerspectiveTransform + & ~QPaintEngine::ConicalGradientFill + & ~QPaintEngine::LinearGradientFill + & ~QPaintEngine::RadialGradientFill + & ~QPaintEngine::BrushStroke); +} + +QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine() +: QPaintEngine(*(new QCoreGraphicsPaintEnginePrivate), qt_mac_cg_features()) +{ +} + +QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr) +: QPaintEngine(dptr, qt_mac_cg_features()) +{ +} + +QCoreGraphicsPaintEngine::~QCoreGraphicsPaintEngine() +{ +} + +bool +QCoreGraphicsPaintEngine::begin(QPaintDevice *pdev) +{ + Q_D(QCoreGraphicsPaintEngine); + if (isActive()) { // already active painting + qWarning("QCoreGraphicsPaintEngine::begin: Painter already active"); + return false; + } + + //initialization + d->pdev = pdev; + d->complexXForm = false; + d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth; + d->cosmeticPenSize = 1; + d->current.clipEnabled = false; + d->pixelSize = QPoint(1,1); + + if (pdev->devType() != QInternal::Printer) { + QMacCGContext ctx(pdev); + d->hd = CGContextRetain(ctx); + if (d->hd) { + d->saveGraphicsState(); + d->orig_xform = CGContextGetCTM(d->hd); + if (d->shading) { + CGShadingRelease(d->shading); + d->shading = nullptr; + } + d->setClip(nullptr); //clear the context's clipping + } + } + + setActive(true); + + if (d->pdev->devType() == QInternal::Widget) { // device is a widget + QWidget *w = (QWidget*)d->pdev; + bool unclipped = w->testAttribute(Qt::WA_PaintUnclipped); + + if ((w->windowType() == Qt::Desktop)) { + if (!unclipped) + qWarning("QCoreGraphicsPaintEngine::begin: Does not support clipped desktop on OS X"); + // ## need to do [qt_mac_window_for(w) makeKeyAndOrderFront]; (need to rename the file) + } else if (unclipped) { + qWarning("QCoreGraphicsPaintEngine::begin: Does not support unclipped painting"); + } + } else if (d->pdev->devType() == QInternal::Pixmap) { // device is a pixmap + QPixmap *pm = (QPixmap*)d->pdev; + if (pm->isNull()) { + qWarning("QCoreGraphicsPaintEngine::begin: Cannot paint null pixmap"); + end(); + return false; + } + } + + setDirty(QPaintEngine::DirtyPen); + setDirty(QPaintEngine::DirtyBrush); + setDirty(QPaintEngine::DirtyBackground); + setDirty(QPaintEngine::DirtyHints); + return true; +} + +bool +QCoreGraphicsPaintEngine::end() +{ + Q_D(QCoreGraphicsPaintEngine); + setActive(false); + if (d->pdev->devType() == QInternal::Widget && static_cast(d->pdev)->windowType() == Qt::Desktop) { + // ### need to do [qt_mac_window_for(static_cast(d->pdev)) orderOut]; (need to rename) + } + if (d->shading) { + CGShadingRelease(d->shading); + d->shading = 0; + } + d->pdev = nullptr; + if (d->hd) { + d->restoreGraphicsState(); + CGContextSynchronize(d->hd); + CGContextRelease(d->hd); + d->hd = nullptr; + } + return true; +} + +void +QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state) +{ + Q_D(QCoreGraphicsPaintEngine); + QPaintEngine::DirtyFlags flags = state.state(); + + if (flags & DirtyTransform) + updateMatrix(state.transform()); + + if (flags & DirtyClipEnabled) { + if (state.isClipEnabled()) + updateClipPath(painter()->clipPath(), Qt::ReplaceClip); + else + updateClipPath(QPainterPath(), Qt::NoClip); + } + + if (flags & DirtyClipPath) { + updateClipPath(state.clipPath(), state.clipOperation()); + } else if (flags & DirtyClipRegion) { + updateClipRegion(state.clipRegion(), state.clipOperation()); + } + + // If the clip has changed we need to update all other states + // too, since they are included in the system context on OSX, + // and changing the clip resets that context back to scratch. + if (flags & (DirtyClipPath | DirtyClipRegion | DirtyClipEnabled)) + flags |= AllDirty; + + if (flags & DirtyPen) + updatePen(state.pen()); + if (flags & (DirtyBrush|DirtyBrushOrigin)) + updateBrush(state.brush(), state.brushOrigin()); + if (flags & DirtyFont) + updateFont(state.font()); + if (flags & DirtyOpacity) + updateOpacity(state.opacity()); + if (flags & DirtyHints) + updateRenderHints(state.renderHints()); + if (flags & DirtyCompositionMode) + updateCompositionMode(state.compositionMode()); + + if (flags & (DirtyPen | DirtyTransform | DirtyHints)) { + if (!qt_pen_is_cosmetic(d->current.pen, state.renderHints())) { + d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticNone; + } else if (d->current.transform.m11() < d->current.transform.m22()-1.0 || + d->current.transform.m11() > d->current.transform.m22()+1.0) { + d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath; + d->cosmeticPenSize = d->adjustPenWidth(d->current.pen.widthF()); + if (!d->cosmeticPenSize) + d->cosmeticPenSize = 1.0; + } else { + d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth; + static const float sqrt2 = std::sqrt(2.0f); + qreal width = d->current.pen.widthF(); + if (!width) + width = 1; + d->cosmeticPenSize = std::sqrt(std::pow(d->pixelSize.y(), 2) + std::pow(d->pixelSize.x(), 2)) / sqrt2 * width; + } + } +} + +void +QCoreGraphicsPaintEngine::updatePen(const QPen &pen) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + d->current.pen = pen; + d->setStrokePen(pen); +} + +void +QCoreGraphicsPaintEngine::updateBrush(const QBrush &brush, const QPointF &brushOrigin) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + d->current.brush = brush; + +#ifdef QT_MAC_USE_NATIVE_GRADIENTS + // Quartz supports only pad spread + if (const QGradient *gradient = brush.gradient()) { + if (drawGradientNatively(gradient)) { + gccaps |= QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill; + } else { + gccaps &= ~(QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill); + } + } +#endif + + if (d->shading) { + CGShadingRelease(d->shading); + d->shading = nullptr; + } + d->setFillBrush(brushOrigin); +} + +void +QCoreGraphicsPaintEngine::updateOpacity(qreal opacity) +{ + Q_D(QCoreGraphicsPaintEngine); + CGContextSetAlpha(d->hd, opacity); +} + +void +QCoreGraphicsPaintEngine::updateFont(const QFont &) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + updatePen(d->current.pen); +} + +void +QCoreGraphicsPaintEngine::updateMatrix(const QTransform &transform) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + + if (qt_is_nan(transform.m11()) || qt_is_nan(transform.m12()) || qt_is_nan(transform.m13()) + || qt_is_nan(transform.m21()) || qt_is_nan(transform.m22()) || qt_is_nan(transform.m23()) + || qt_is_nan(transform.m31()) || qt_is_nan(transform.m32()) || qt_is_nan(transform.m33())) + return; + + d->current.transform = transform; + d->setTransform(transform.isIdentity() ? 0 : &transform); + d->complexXForm = (transform.m11() != 1 || transform.m22() != 1 + || transform.m12() != 0 || transform.m21() != 0); + d->pixelSize = d->devicePixelSize(d->hd); +} + +void +QCoreGraphicsPaintEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + if (op == Qt::NoClip) { + if (d->current.clipEnabled) { + d->current.clipEnabled = false; + d->current.clip = QRegion(); + d->setClip(nullptr); + } + } else { + if (!d->current.clipEnabled) + op = Qt::ReplaceClip; + d->current.clipEnabled = true; + QRegion clipRegion(p.toFillPolygon().toPolygon(), p.fillRule()); + if (op == Qt::ReplaceClip) { + d->current.clip = clipRegion; + d->setClip(nullptr); + if (p.isEmpty()) { + CGRect rect = CGRectMake(0, 0, 0, 0); + CGContextClipToRect(d->hd, rect); + } else { + CGMutablePathRef path = qt_mac_compose_path(p); + CGContextBeginPath(d->hd); + CGContextAddPath(d->hd, path); + if (p.fillRule() == Qt::WindingFill) + CGContextClip(d->hd); + else + CGContextEOClip(d->hd); + CGPathRelease(path); + } + } else if (op == Qt::IntersectClip) { + d->current.clip = d->current.clip.intersected(clipRegion); + d->setClip(&d->current.clip); + } + } +} + +void +QCoreGraphicsPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + if (op == Qt::NoClip) { + d->current.clipEnabled = false; + d->current.clip = QRegion(); + d->setClip(nullptr); + } else { + if (!d->current.clipEnabled) + op = Qt::ReplaceClip; + d->current.clipEnabled = true; + if (op == Qt::IntersectClip) + d->current.clip = d->current.clip.intersected(clipRegion); + else if (op == Qt::ReplaceClip) + d->current.clip = clipRegion; + d->setClip(&d->current.clip); + } +} + +void +QCoreGraphicsPaintEngine::drawPath(const QPainterPath &p) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + + if (state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + CGMutablePathRef path = qt_mac_compose_path(p); + uchar ops = QCoreGraphicsPaintEnginePrivate::CGStroke; + if (p.fillRule() == Qt::WindingFill) + ops |= QCoreGraphicsPaintEnginePrivate::CGFill; + else + ops |= QCoreGraphicsPaintEnginePrivate::CGEOFill; + CGContextBeginPath(d->hd); + d->drawPath(ops, path); + CGPathRelease(path); +} + +void +QCoreGraphicsPaintEngine::drawRects(const QRectF *rects, int rectCount) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + + if (state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + for (int i=0; idrawPath(QCoreGraphicsPaintEnginePrivate::CGFill|QCoreGraphicsPaintEnginePrivate::CGStroke, + path); + CGPathRelease(path); + } +} + +void +QCoreGraphicsPaintEngine::drawPoints(const QPointF *points, int pointCount) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + + if (state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + if (d->current.pen.capStyle() == Qt::FlatCap) + CGContextSetLineCap(d->hd, kCGLineCapSquare); + + CGMutablePathRef path = CGPathCreateMutable(); + for (int i=0; i < pointCount; i++) { + float x = points[i].x(), y = points[i].y(); + CGPathMoveToPoint(path, nullptr, x, y); + CGPathAddLineToPoint(path, nullptr, x+0.001, y); + } + + bool doRestore = false; + if (d->cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticNone && !(state->renderHints() & QPainter::Antialiasing)) { + //we don't want adjusted pens for point rendering + doRestore = true; + d->saveGraphicsState(); + CGContextSetLineWidth(d->hd, d->current.pen.widthF()); + } + d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path); + if (doRestore) + d->restoreGraphicsState(); + CGPathRelease(path); + if (d->current.pen.capStyle() == Qt::FlatCap) + CGContextSetLineCap(d->hd, kCGLineCapButt); +} + +void +QCoreGraphicsPaintEngine::drawEllipse(const QRectF &r) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + + if (state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + CGMutablePathRef path = CGPathCreateMutable(); + CGAffineTransform transform = CGAffineTransformMakeScale(r.width() / r.height(), 1); + CGPathAddArc(path, &transform,(r.x() + (r.width() / 2)) / (r.width() / r.height()), + r.y() + (r.height() / 2), r.height() / 2, 0, (2 * M_PI), false); + d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill | QCoreGraphicsPaintEnginePrivate::CGStroke, + path); + CGPathRelease(path); +} + +void +QCoreGraphicsPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + + if (state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + CGMutablePathRef path = CGPathCreateMutable(); + CGPathMoveToPoint(path, nullptr, points[0].x(), points[0].y()); + for (int x = 1; x < pointCount; ++x) + CGPathAddLineToPoint(path, nullptr, points[x].x(), points[x].y()); + if (mode != PolylineMode && points[0] != points[pointCount-1]) + CGPathAddLineToPoint(path, nullptr, points[0].x(), points[0].y()); + uint op = QCoreGraphicsPaintEnginePrivate::CGStroke; + if (mode != PolylineMode) + op |= mode == OddEvenMode ? QCoreGraphicsPaintEnginePrivate::CGEOFill + : QCoreGraphicsPaintEnginePrivate::CGFill; + d->drawPath(op, path); + CGPathRelease(path); +} + +void +QCoreGraphicsPaintEngine::drawLines(const QLineF *lines, int lineCount) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + + if (state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + CGMutablePathRef path = CGPathCreateMutable(); + for (int i = 0; i < lineCount; i++) { + const QPointF start = lines[i].p1(), end = lines[i].p2(); + CGPathMoveToPoint(path, nullptr, start.x(), start.y()); + CGPathAddLineToPoint(path, nullptr, end.x(), end.y()); + } + d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path); + CGPathRelease(path); +} + +void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + + if (state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + if (pm.isNull()) + return; + + bool differentSize = (QRectF(0, 0, pm.width(), pm.height()) != sr), doRestore = false; + CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); + QCFType image; + bool isBitmap = (pm.depth() == 1); + if (isBitmap) { + doRestore = true; + d->saveGraphicsState(); + + const QColor &col = d->current.pen.color(); + CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col)); + image = qt_mac_create_imagemask(pm, sr); + } else if (differentSize) { + QCFType img = qt_mac_toCGImage(pm.toImage()); + if (img) + image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height()))); + } else { + image = qt_mac_toCGImage(pm.toImage()); + } + qt_mac_drawCGImage(d->hd, &rect, image); + if (doRestore) + d->restoreGraphicsState(); +} + +void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr, + Qt::ImageConversionFlags flags) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_UNUSED(flags); + Q_ASSERT(isActive()); + + if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + QCFType cgimage = qt_mac_toCGImage(img); + CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); + if (QRectF(0, 0, img.width(), img.height()) != sr) + cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(), + sr.width(), sr.height())); + qt_mac_drawCGImage(d->hd, &rect, cgimage); +} + +void QCoreGraphicsPaintEngine::initialize() +{ +} + +void QCoreGraphicsPaintEngine::cleanup() +{ +} + +CGContextRef +QCoreGraphicsPaintEngine::handle() const +{ + return d_func()->hd; +} + +void +QCoreGraphicsPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, + const QPointF &p) +{ + Q_D(QCoreGraphicsPaintEngine); + Q_ASSERT(isActive()); + + if (state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + //save the old state + d->saveGraphicsState(); + + //setup the pattern + QMacPattern *qpattern = new QMacPattern; + qpattern->data.pixmap = pixmap; + qpattern->foreground = d->current.pen.color(); + qpattern->pdev = d->pdev; + CGPatternCallbacks callbks; + callbks.version = 0; + callbks.drawPattern = qt_mac_draw_pattern; + callbks.releaseInfo = qt_mac_dispose_pattern; + const int width = qpattern->width(), height = qpattern->height(); + CGAffineTransform trans = CGContextGetCTM(d->hd); + CGPatternRef pat = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height), + trans, width, height, + kCGPatternTilingNoDistortion, true, &callbks); + CGColorSpaceRef cs = CGColorSpaceCreatePattern(nullptr); + CGContextSetFillColorSpace(d->hd, cs); + CGFloat component = 1.0; //just one + CGContextSetFillPattern(d->hd, pat, &component); + CGSize phase = CGSizeApplyAffineTransform(CGSizeMake(-(p.x()-r.x()), -(p.y()-r.y())), trans); + CGContextSetPatternPhase(d->hd, phase); + + //fill the rectangle + CGRect mac_rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); + CGContextFillRect(d->hd, mac_rect); + + //restore the state + d->restoreGraphicsState(); + //cleanup + CGColorSpaceRelease(cs); + CGPatternRelease(pat); +} + +void QCoreGraphicsPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &item) +{ + Q_D(QCoreGraphicsPaintEngine); + if (d->current.transform.type() == QTransform::TxProject +#ifndef QMAC_NATIVE_GRADIENTS + || painter()->pen().brush().gradient() //Just let the base engine "emulate" the gradient +#endif + ) { + QPaintEngine::drawTextItem(pos, item); + return; + } + + if (state->compositionMode() == QPainter::CompositionMode_Destination) + return; + + const QTextItemInt &ti = static_cast(item); + + QPen oldPen = painter()->pen(); + QBrush oldBrush = painter()->brush(); + QPointF oldBrushOrigin = painter()->brushOrigin(); + updatePen(Qt::NoPen); + updateBrush(oldPen.brush(), QPointF(0, 0)); + + Q_ASSERT(type() == QPaintEngine::CoreGraphics); + + QFontEngine *fe = ti.fontEngine; + + const bool textAA = ((state->renderHints() & QPainter::TextAntialiasing) + && !(fe->fontDef.styleStrategy & QFont::NoAntialias)); + const bool lineAA = state->renderHints() & QPainter::Antialiasing; + if (textAA != lineAA) + CGContextSetShouldAntialias(d->hd, textAA); + + const bool smoothing = textAA && !(fe->fontDef.styleStrategy & QFont::NoSubpixelAntialias); + if (d->disabledSmoothFonts == smoothing) + CGContextSetShouldSmoothFonts(d->hd, smoothing); + + if (ti.glyphs.numGlyphs) { + switch (fe->type()) { + case QFontEngine::Mac: + static_cast(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height()); + break; + case QFontEngine::Box: + d->drawBoxTextItem(pos, ti); + break; + default: + break; + } + } + + if (textAA != lineAA) + CGContextSetShouldAntialias(d->hd, !textAA); + + if (smoothing == d->disabledSmoothFonts) + CGContextSetShouldSmoothFonts(d->hd, !d->disabledSmoothFonts); + + updatePen(oldPen); + updateBrush(oldBrush, oldBrushOrigin); +} + +QPainter::RenderHints +QCoreGraphicsPaintEngine::supportedRenderHints() const +{ + return QPainter::RenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); +} +enum CGCompositeMode { + kCGCompositeModeClear = 0, + kCGCompositeModeCopy = 1, + kCGCompositeModeSourceOver = 2, + kCGCompositeModeSourceIn = 3, + kCGCompositeModeSourceOut = 4, + kCGCompositeModeSourceAtop = 5, + kCGCompositeModeDestinationOver = 6, + kCGCompositeModeDestinationIn = 7, + kCGCompositeModeDestinationOut = 8, + kCGCompositeModeDestinationAtop = 9, + kCGCompositeModeXOR = 10, + kCGCompositeModePlusDarker = 11, // (max (0, (1-d) + (1-s))) + kCGCompositeModePlusLighter = 12, // (min (1, s + d)) + }; +extern "C" { + extern void CGContextSetCompositeOperation(CGContextRef, int); +} // private function, but is in all versions of OS X. +void +QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) +{ + int cg_mode = kCGBlendModeNormal; + switch (mode) { + case QPainter::CompositionMode_Multiply: + cg_mode = kCGBlendModeMultiply; + break; + case QPainter::CompositionMode_Screen: + cg_mode = kCGBlendModeScreen; + break; + case QPainter::CompositionMode_Overlay: + cg_mode = kCGBlendModeOverlay; + break; + case QPainter::CompositionMode_Darken: + cg_mode = kCGBlendModeDarken; + break; + case QPainter::CompositionMode_Lighten: + cg_mode = kCGBlendModeLighten; + break; + case QPainter::CompositionMode_ColorDodge: + cg_mode = kCGBlendModeColorDodge; + break; + case QPainter::CompositionMode_ColorBurn: + cg_mode = kCGBlendModeColorBurn; + break; + case QPainter::CompositionMode_HardLight: + cg_mode = kCGBlendModeHardLight; + break; + case QPainter::CompositionMode_SoftLight: + cg_mode = kCGBlendModeSoftLight; + break; + case QPainter::CompositionMode_Difference: + cg_mode = kCGBlendModeDifference; + break; + case QPainter::CompositionMode_Exclusion: + cg_mode = kCGBlendModeExclusion; + break; + case QPainter::CompositionMode_Plus: + cg_mode = kCGBlendModePlusLighter; + break; + case QPainter::CompositionMode_SourceOver: + cg_mode = kCGBlendModeNormal; + break; + case QPainter::CompositionMode_DestinationOver: + cg_mode = kCGBlendModeDestinationOver; + break; + case QPainter::CompositionMode_Clear: + cg_mode = kCGBlendModeClear; + break; + case QPainter::CompositionMode_Source: + cg_mode = kCGBlendModeCopy; + break; + case QPainter::CompositionMode_Destination: + cg_mode = -1; + break; + case QPainter::CompositionMode_SourceIn: + cg_mode = kCGBlendModeSourceIn; + break; + case QPainter::CompositionMode_DestinationIn: + cg_mode = kCGCompositeModeDestinationIn; + break; + case QPainter::CompositionMode_SourceOut: + cg_mode = kCGBlendModeSourceOut; + break; + case QPainter::CompositionMode_DestinationOut: + cg_mode = kCGBlendModeDestinationOver; + break; + case QPainter::CompositionMode_SourceAtop: + cg_mode = kCGBlendModeSourceAtop; + break; + case QPainter::CompositionMode_DestinationAtop: + cg_mode = kCGBlendModeDestinationAtop; + break; + case QPainter::CompositionMode_Xor: + cg_mode = kCGBlendModeXOR; + break; + default: + break; + } + if (cg_mode > -1) { + CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode)); + } +} + +void +QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints) +{ + Q_D(QCoreGraphicsPaintEngine); + CGContextSetShouldAntialias(d->hd, hints & QPainter::Antialiasing); + CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? + kCGInterpolationHigh : kCGInterpolationNone); + bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing; + if (!textAntialiasing || d->disabledSmoothFonts) { + d->disabledSmoothFonts = !textAntialiasing; + CGContextSetShouldSmoothFonts(d->hd, textAntialiasing); + } +} + +/* + Returns the size of one device pixel in user-space coordinates. +*/ +QPointF QCoreGraphicsPaintEnginePrivate::devicePixelSize(CGContextRef) +{ + QPointF p1 = current.transform.inverted().map(QPointF(0, 0)); + QPointF p2 = current.transform.inverted().map(QPointF(1, 1)); + return QPointF(qAbs(p2.x() - p1.x()), qAbs(p2.y() - p1.y())); +} + +/* + Adjusts the pen width so we get correct line widths in the + non-transformed, aliased case. +*/ +float QCoreGraphicsPaintEnginePrivate::adjustPenWidth(float penWidth) +{ + Q_Q(QCoreGraphicsPaintEngine); + float ret = penWidth; + if (!complexXForm && !(q->state->renderHints() & QPainter::Antialiasing)) { + if (penWidth < 2) + ret = 1; + else if (penWidth < 3) + ret = 1.5; + else + ret = penWidth -1; + } + return ret; +} + +void +QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen) +{ + //pencap + CGLineCap cglinecap = kCGLineCapButt; + if (pen.capStyle() == Qt::SquareCap) + cglinecap = kCGLineCapSquare; + else if (pen.capStyle() == Qt::RoundCap) + cglinecap = kCGLineCapRound; + CGContextSetLineCap(hd, cglinecap); + CGContextSetLineWidth(hd, adjustPenWidth(pen.widthF())); + + //join + CGLineJoin cglinejoin = kCGLineJoinMiter; + if (pen.joinStyle() == Qt::BevelJoin) + cglinejoin = kCGLineJoinBevel; + else if (pen.joinStyle() == Qt::RoundJoin) + cglinejoin = kCGLineJoinRound; + CGContextSetLineJoin(hd, cglinejoin); +// CGContextSetMiterLimit(hd, pen.miterLimit()); + + //pen style + QVector linedashes; + if (pen.style() == Qt::CustomDashLine) { + QVector customs = pen.dashPattern(); + for (int i = 0; i < customs.size(); ++i) + linedashes.append(customs.at(i)); + } else if (pen.style() == Qt::DashLine) { + linedashes.append(4); + linedashes.append(2); + } else if (pen.style() == Qt::DotLine) { + linedashes.append(1); + linedashes.append(2); + } else if (pen.style() == Qt::DashDotLine) { + linedashes.append(4); + linedashes.append(2); + linedashes.append(1); + linedashes.append(2); + } else if (pen.style() == Qt::DashDotDotLine) { + linedashes.append(4); + linedashes.append(2); + linedashes.append(1); + linedashes.append(2); + linedashes.append(1); + linedashes.append(2); + } + const CGFloat cglinewidth = pen.widthF() <= 0.0f ? 1.0f : float(pen.widthF()); + for (int i = 0; i < linedashes.size(); ++i) { + linedashes[i] *= cglinewidth; + if (cglinewidth < 3 && (cglinecap == kCGLineCapSquare || cglinecap == kCGLineCapRound)) { + if ((i%2)) + linedashes[i] += cglinewidth/2; + else + linedashes[i] -= cglinewidth/2; + } + } + CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size()); + + // color + CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color())); +} + +// Add our own patterns here to deal with the fact that the coordinate system +// is flipped vertically with Quartz2D. +static const uchar *qt_mac_patternForBrush(int brushStyle) +{ + Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern); + static const uchar dense1_pat[] = { 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00 }; + static const uchar dense2_pat[] = { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 }; + static const uchar dense3_pat[] = { 0x11, 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa }; + static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }; + static const uchar dense5_pat[] = { 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55 }; + static const uchar dense6_pat[] = { 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 }; + static const uchar dense7_pat[] = { 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff }; + static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff }; + static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef }; + static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef }; + static const uchar fdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe }; + static const uchar bdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f }; + static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e }; + static const uchar *const pat_tbl[] = { + dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat, + dense6_pat, dense7_pat, + hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat }; + return pat_tbl[brushStyle - Qt::Dense1Pattern]; +} + +void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset) +{ + // pattern + Qt::BrushStyle bs = current.brush.style(); +#ifdef QT_MAC_USE_NATIVE_GRADIENTS + if (bs == Qt::LinearGradientPattern || bs == Qt::RadialGradientPattern) { + const QGradient *grad = static_cast(current.brush.gradient()); + if (drawGradientNatively(grad)) { + Q_ASSERT(grad->spread() == QGradient::PadSpread); + + static const CGFloat domain[] = { 0.0f, +1.0f }; + static const CGFunctionCallbacks callbacks = { 0, qt_mac_color_gradient_function, nullptr }; + CGFunctionRef fill_func = CGFunctionCreate(reinterpret_cast(¤t.brush), + 1, domain, 4, nullptr, &callbacks); + + CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB) + if (bs == Qt::LinearGradientPattern) { + const QLinearGradient *linearGrad = static_cast(grad); + const QPointF start(linearGrad->start()); + const QPointF stop(linearGrad->finalStop()); + shading = CGShadingCreateAxial(colorspace, CGPointMake(start.x(), start.y()), + CGPointMake(stop.x(), stop.y()), fill_func, true, true); + } else { + Q_ASSERT(bs == Qt::RadialGradientPattern); + const QRadialGradient *radialGrad = static_cast(grad); + QPointF center(radialGrad->center()); + QPointF focal(radialGrad->focalPoint()); + qreal radius = radialGrad->radius(); + qreal focalRadius = radialGrad->focalRadius(); + shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()), + focalRadius, CGPointMake(center.x(), center.y()), radius, fill_func, false, true); + } + + CGFunctionRelease(fill_func); + } + } else +#endif + if (bs != Qt::SolidPattern && bs != Qt::NoBrush +#ifndef QT_MAC_USE_NATIVE_GRADIENTS + && (bs < Qt::LinearGradientPattern || bs > Qt::ConicalGradientPattern) +#endif + ) + { + QMacPattern *qpattern = new QMacPattern; + qpattern->pdev = pdev; + CGFloat components[4] = { 1.0, 1.0, 1.0, 1.0 }; + CGColorSpaceRef base_colorspace = nullptr; + if (bs == Qt::TexturePattern) { + qpattern->data.pixmap = current.brush.texture(); + if (qpattern->data.pixmap.isQBitmap()) { + const QColor &col = current.brush.color(); + components[0] = qt_mac_convert_color_to_cg(col.red()); + components[1] = qt_mac_convert_color_to_cg(col.green()); + components[2] = qt_mac_convert_color_to_cg(col.blue()); + base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + } + } else { + qpattern->as_mask = true; + + qpattern->data.bytes = qt_mac_patternForBrush(bs); + const QColor &col = current.brush.color(); + components[0] = qt_mac_convert_color_to_cg(col.red()); + components[1] = qt_mac_convert_color_to_cg(col.green()); + components[2] = qt_mac_convert_color_to_cg(col.blue()); + base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + } + int width = qpattern->width(), height = qpattern->height(); + qpattern->foreground = current.brush.color(); + + CGColorSpaceRef fill_colorspace = CGColorSpaceCreatePattern(base_colorspace); + CGContextSetFillColorSpace(hd, fill_colorspace); + + CGAffineTransform xform = CGContextGetCTM(hd); + xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(current.brush.transform()), xform); + xform = CGAffineTransformTranslate(xform, offset.x(), offset.y()); + + CGPatternCallbacks callbks; + callbks.version = 0; + callbks.drawPattern = qt_mac_draw_pattern; + callbks.releaseInfo = qt_mac_dispose_pattern; + CGPatternRef fill_pattern = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height), + xform, width, height, kCGPatternTilingNoDistortion, + !base_colorspace, &callbks); + CGContextSetFillPattern(hd, fill_pattern, components); + + + CGPatternRelease(fill_pattern); + CGColorSpaceRelease(base_colorspace); + CGColorSpaceRelease(fill_colorspace); + } else if (bs != Qt::NoBrush) { + CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color())); + } +} + +void +QCoreGraphicsPaintEnginePrivate::setClip(const QRegion *rgn) +{ + Q_Q(QCoreGraphicsPaintEngine); + if (hd) { + resetClip(); + QRegion sysClip = q->systemClip(); + if (!sysClip.isEmpty()) + qt_mac_clip_cg(hd, sysClip, &orig_xform); + if (rgn) + qt_mac_clip_cg(hd, *rgn, nullptr); + } +} + +struct qt_mac_cg_transform_path { + CGMutablePathRef path; + CGAffineTransform transform; +}; + +void qt_mac_cg_transform_path_apply(void *info, const CGPathElement *element) +{ + Q_ASSERT(info && element); + qt_mac_cg_transform_path *t = (qt_mac_cg_transform_path*)info; + switch (element->type) { + case kCGPathElementMoveToPoint: + CGPathMoveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y); + break; + case kCGPathElementAddLineToPoint: + CGPathAddLineToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y); + break; + case kCGPathElementAddQuadCurveToPoint: + CGPathAddQuadCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y, + element->points[1].x, element->points[1].y); + break; + case kCGPathElementAddCurveToPoint: + CGPathAddCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y, + element->points[1].x, element->points[1].y, + element->points[2].x, element->points[2].y); + break; + case kCGPathElementCloseSubpath: + CGPathCloseSubpath(t->path); + break; + default: + qDebug() << "Unhandled path transform type: " << element->type; + } +} + +void QCoreGraphicsPaintEnginePrivate::drawPath(uchar ops, CGMutablePathRef path) +{ + Q_Q(QCoreGraphicsPaintEngine); + Q_ASSERT((ops & (CGFill | CGEOFill)) != (CGFill | CGEOFill)); //can't really happen + if ((ops & (CGFill | CGEOFill))) { + if (shading) { + Q_ASSERT(path); + CGContextBeginPath(hd); + CGContextAddPath(hd, path); + saveGraphicsState(); + if (ops & CGFill) + CGContextClip(hd); + else if (ops & CGEOFill) + CGContextEOClip(hd); + if (current.brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode) { + CGRect boundingBox = CGPathGetBoundingBox(path); + CGContextConcatCTM(hd, + CGAffineTransformMake(boundingBox.size.width, 0, + 0, boundingBox.size.height, + boundingBox.origin.x, boundingBox.origin.y)); + } + CGContextDrawShading(hd, shading); + restoreGraphicsState(); + ops &= ~CGFill; + ops &= ~CGEOFill; + } else if (current.brush.style() == Qt::NoBrush) { + ops &= ~CGFill; + ops &= ~CGEOFill; + } + } + if ((ops & CGStroke) && current.pen.style() == Qt::NoPen) + ops &= ~CGStroke; + + if (ops & (CGEOFill | CGFill)) { + CGContextBeginPath(hd); + CGContextAddPath(hd, path); + if (ops & CGEOFill) { + CGContextEOFillPath(hd); + } else { + CGContextFillPath(hd); + } + } + + // Avoid saving and restoring the context if we can. + const bool needContextSave = (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone || + !(q->state->renderHints() & QPainter::Antialiasing)); + if (ops & CGStroke) { + if (needContextSave) + saveGraphicsState(); + CGContextBeginPath(hd); + + // Translate a fraction of a pixel size in the y direction + // to make sure that primitives painted at pixel borders + // fills the right pixel. This is needed since the y xais + // in the Quartz coordinate system is inverted compared to Qt. + if (!(q->state->renderHints() & QPainter::Antialiasing)) { + if (current.pen.style() == Qt::SolidLine || current.pen.width() >= 3) + CGContextTranslateCTM(hd, double(pixelSize.x()) * 0.25, double(pixelSize.y()) * 0.25); + else + CGContextTranslateCTM(hd, 0, double(pixelSize.y()) * 0.1); + } + + if (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone) { + // If antialiazing is enabled, use the cosmetic pen size directly. + if (q->state->renderHints() & QPainter::Antialiasing) + CGContextSetLineWidth(hd, cosmeticPenSize); + else if (current.pen.widthF() <= 1) + CGContextSetLineWidth(hd, cosmeticPenSize * 0.9f); + else + CGContextSetLineWidth(hd, cosmeticPenSize); + } + if (cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath) { + qt_mac_cg_transform_path t; + t.transform = qt_mac_convert_transform_to_cg(current.transform); + t.path = CGPathCreateMutable(); + CGPathApply(path, &t, qt_mac_cg_transform_path_apply); //transform the path + setTransform(nullptr); //unset the context transform + CGContextSetLineWidth(hd, cosmeticPenSize); + CGContextAddPath(hd, t.path); + CGPathRelease(t.path); + } else { + CGContextAddPath(hd, path); + } + + CGContextStrokePath(hd); + if (needContextSave) + restoreGraphicsState(); + } +} + +QT_END_NAMESPACE diff --git a/src/printsupport/platform/macos/qpaintengine_mac_p.h b/src/printsupport/platform/macos/qpaintengine_mac_p.h new file mode 100644 index 0000000000..5ee158e8e5 --- /dev/null +++ b/src/printsupport/platform/macos/qpaintengine_mac_p.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** 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 QPAINTENGINE_MAC_P_H +#define QPAINTENGINE_MAC_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 + +#include +#include +#include +#include +#include + +typedef struct CGColorSpace *CGColorSpaceRef; +typedef struct CGContext *CGContextRef; + +QT_BEGIN_NAMESPACE + +class QCoreGraphicsPaintEnginePrivate; +class QCoreGraphicsPaintEngine : public QPaintEngine +{ + Q_DECLARE_PRIVATE(QCoreGraphicsPaintEngine) + +public: + QCoreGraphicsPaintEngine(); + ~QCoreGraphicsPaintEngine(); + + bool begin(QPaintDevice *pdev); + bool end(); + + void updateState(const QPaintEngineState &state); + + void updatePen(const QPen &pen); + void updateBrush(const QBrush &brush, const QPointF &pt); + void updateFont(const QFont &font); + void updateOpacity(qreal opacity); + void updateMatrix(const QTransform &matrix); + void updateTransform(const QTransform &matrix); + void updateClipRegion(const QRegion ®ion, Qt::ClipOperation op); + void updateClipPath(const QPainterPath &path, Qt::ClipOperation op); + void updateCompositionMode(QPainter::CompositionMode mode); + void updateRenderHints(QPainter::RenderHints hints); + + void drawLines(const QLineF *lines, int lineCount); + void drawRects(const QRectF *rects, int rectCount); + void drawPoints(const QPointF *p, int pointCount); + void drawEllipse(const QRectF &r); + void drawPath(const QPainterPath &path); + + void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); + void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); + + void drawTextItem(const QPointF &pos, const QTextItem &item); + void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags flags = Qt::AutoColor); + + Type type() const { return QPaintEngine::CoreGraphics; } + + CGContextRef handle() const; + + static void initialize(); + static void cleanup(); + + QPainter::RenderHints supportedRenderHints() const; + + //avoid partial shadowed overload warnings... + void drawLines(const QLine *lines, int lineCount) { QPaintEngine::drawLines(lines, lineCount); } + void drawRects(const QRect *rects, int rectCount) { QPaintEngine::drawRects(rects, rectCount); } + void drawPoints(const QPoint *p, int pointCount) { QPaintEngine::drawPoints(p, pointCount); } + void drawEllipse(const QRect &r) { QPaintEngine::drawEllipse(r); } + void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) + { QPaintEngine::drawPolygon(points, pointCount, mode); } + +protected: + friend class QMacPrintEngine; + friend class QMacPrintEnginePrivate; + QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr); + +private: + Q_DISABLE_COPY(QCoreGraphicsPaintEngine) +}; + +/***************************************************************************** + Private data + *****************************************************************************/ +class QCoreGraphicsPaintEnginePrivate : public QPaintEnginePrivate +{ + Q_DECLARE_PUBLIC(QCoreGraphicsPaintEngine) +public: + QCoreGraphicsPaintEnginePrivate() + : hd(nullptr), shading(nullptr), stackCount(0), complexXForm(false), disabledSmoothFonts(false) + { + } + + struct { + QPen pen; + QBrush brush; + uint clipEnabled : 1; + QRegion clip; + QTransform transform; + } current; + + //state info (shared with QD) + CGAffineTransform orig_xform; + + //cg structures + CGContextRef hd; + CGShadingRef shading; + int stackCount; + bool complexXForm; + bool disabledSmoothFonts; + enum { CosmeticNone, CosmeticTransformPath, CosmeticSetPenWidth } cosmeticPen; + + // pixel and cosmetic pen size in user coordinates. + QPointF pixelSize; + float cosmeticPenSize; + + //internal functions + enum { CGStroke=0x01, CGEOFill=0x02, CGFill=0x04 }; + void drawPath(uchar ops, CGMutablePathRef path = nullptr); + void setClip(const QRegion *rgn = nullptr); + void resetClip(); + void setFillBrush(const QPointF &origin=QPoint()); + void setStrokePen(const QPen &pen); + inline void saveGraphicsState(); + inline void restoreGraphicsState(); + float penOffset(); + QPointF devicePixelSize(CGContextRef context); + float adjustPenWidth(float penWidth); + inline void setTransform(const QTransform *matrix = nullptr) + { + CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd))); + CGAffineTransform xform = orig_xform; + if (matrix) { + extern CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &); + xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(*matrix), xform); + } + CGContextConcatCTM(hd, xform); + CGContextSetTextMatrix(hd, xform); + } +}; + +inline void QCoreGraphicsPaintEnginePrivate::saveGraphicsState() +{ + ++stackCount; + CGContextSaveGState(hd); +} + +inline void QCoreGraphicsPaintEnginePrivate::restoreGraphicsState() +{ + --stackCount; + Q_ASSERT(stackCount >= 0); + CGContextRestoreGState(hd); +} + +QT_END_NAMESPACE + +#endif // QPAINTENGINE_MAC_P_H diff --git a/src/printsupport/platform/macos/qprintengine_mac.mm b/src/printsupport/platform/macos/qprintengine_mac.mm new file mode 100644 index 0000000000..1b06722447 --- /dev/null +++ b/src/printsupport/platform/macos/qprintengine_mac.mm @@ -0,0 +1,805 @@ +/**************************************************************************** +** +** 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 +#include + +#include "qprintengine_mac_p.h" +#include "qcocoaprintersupport_p.h" +#include +#include +#include +#include + +#include + +#ifndef QT_NO_PRINTER + +QT_BEGIN_NAMESPACE + +extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits); + +QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode, const QString &deviceId) + : QPaintEngine(*(new QMacPrintEnginePrivate)) +{ + Q_D(QMacPrintEngine); + d->mode = mode; + QString id = deviceId; + if (id.isEmpty()) + id = QCocoaPrinterSupport().defaultPrintDeviceId(); + else + setProperty(QPrintEngine::PPK_PrinterName, deviceId); + d->m_printDevice.reset(new QCocoaPrintDevice(id)); + d->m_pageLayout.setPageSize(d->m_printDevice->defaultPageSize()); + d->initialize(); +} + +bool QMacPrintEngine::begin(QPaintDevice *dev) +{ + Q_D(QMacPrintEngine); + + Q_ASSERT(dev && dev->devType() == QInternal::Printer); + if (!static_cast(dev)->isValid()) + return false; + + if (d->state == QPrinter::Idle && !d->isPrintSessionInitialized()) // Need to reinitialize + d->initialize(); + + d->paintEngine->state = state; + d->paintEngine->begin(dev); + Q_ASSERT_X(d->state == QPrinter::Idle, "QMacPrintEngine", "printer already active"); + + if (PMSessionValidatePrintSettings(d->session(), d->settings(), kPMDontWantBoolean) != noErr + || PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean) != noErr) { + d->state = QPrinter::Error; + return false; + } + + if (!d->outputFilename.isEmpty()) { + QCFType outFile = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, + QCFString(d->outputFilename), + kCFURLPOSIXPathStyle, + false); + if (PMSessionSetDestination(d->session(), d->settings(), kPMDestinationFile, + kPMDocumentFormatPDF, outFile) != noErr) { + qWarning("QMacPrintEngine::begin: Problem setting file [%s]", d->outputFilename.toUtf8().constData()); + return false; + } + } + + OSStatus status = PMSessionBeginCGDocumentNoDialog(d->session(), d->settings(), d->format()); + if (status != noErr) { + d->state = QPrinter::Error; + return false; + } + + d->state = QPrinter::Active; + setActive(true); + d->newPage_helper(); + return true; +} + +bool QMacPrintEngine::end() +{ + Q_D(QMacPrintEngine); + if (d->state == QPrinter::Aborted) + return true; // I was just here a function call ago :) + if (d->paintEngine->type() == QPaintEngine::CoreGraphics) { + // We don't need the paint engine to call restoreGraphicsState() + static_cast(d->paintEngine)->d_func()->stackCount = 0; + static_cast(d->paintEngine)->d_func()->hd = nullptr; + } + d->paintEngine->end(); + if (d->state != QPrinter::Idle) + d->releaseSession(); + d->state = QPrinter::Idle; + return true; +} + +QPaintEngine * +QMacPrintEngine::paintEngine() const +{ + return d_func()->paintEngine; +} + +Qt::HANDLE QMacPrintEngine::handle() const +{ + QCoreGraphicsPaintEngine *cgEngine = static_cast(paintEngine()); + return cgEngine->d_func()->hd; +} + +QMacPrintEnginePrivate::~QMacPrintEnginePrivate() +{ + [printInfo release]; + delete paintEngine; +} + +QPrinter::PrinterState QMacPrintEngine::printerState() const +{ + return d_func()->state; +} + +bool QMacPrintEngine::newPage() +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + OSStatus err = PMSessionEndPageNoDialog(d->session()); + if (err != noErr) { + if (err == kPMCancel) { + // User canceled, we need to abort! + abort(); + } else { + // Not sure what the problem is... + qWarning("QMacPrintEngine::newPage: Cannot end current page. %ld", long(err)); + d->state = QPrinter::Error; + } + return false; + } + return d->newPage_helper(); +} + +bool QMacPrintEngine::abort() +{ + Q_D(QMacPrintEngine); + if (d->state != QPrinter::Active) + return false; + bool ret = end(); + d->state = QPrinter::Aborted; + return ret; +} + +int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const +{ + Q_D(const QMacPrintEngine); + int val = 1; + switch (m) { + case QPaintDevice::PdmWidth: + val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).width(); + break; + case QPaintDevice::PdmHeight: + val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).height(); + break; + case QPaintDevice::PdmWidthMM: + val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).width()); + break; + case QPaintDevice::PdmHeightMM: + val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).height()); + break; + case QPaintDevice::PdmPhysicalDpiX: + case QPaintDevice::PdmPhysicalDpiY: { + PMPrinter printer; + if (PMSessionGetCurrentPrinter(d->session(), &printer) == noErr) { + PMResolution resolution; + PMPrinterGetOutputResolution(printer, d->settings(), &resolution); + val = (int)resolution.vRes; + break; + } + Q_FALLTHROUGH(); + } + case QPaintDevice::PdmDpiY: + val = (int)d->resolution.vRes; + break; + case QPaintDevice::PdmDpiX: + val = (int)d->resolution.hRes; + break; + case QPaintDevice::PdmNumColors: + val = (1 << metric(QPaintDevice::PdmDepth)); + break; + case QPaintDevice::PdmDepth: + val = 24; + break; + case QPaintDevice::PdmDevicePixelRatio: + val = 1; + break; + case QPaintDevice::PdmDevicePixelRatioScaled: + val = 1 * QPaintDevice::devicePixelRatioFScale(); + break; + default: + val = 0; + qWarning("QPrinter::metric: Invalid metric command"); + } + return val; +} + +void QMacPrintEnginePrivate::initialize() +{ + Q_Q(QMacPrintEngine); + + Q_ASSERT(!printInfo); + + if (!paintEngine) + paintEngine = new QCoreGraphicsPaintEngine(); + + q->gccaps = paintEngine->gccaps; + + QMacAutoReleasePool pool; + printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]]; + + QList resolutions = m_printDevice->supportedResolutions(); + if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) { + std::sort(resolutions.begin(), resolutions.end()); + if (resolutions.count() > 1 && mode == QPrinter::HighResolution) + resolution.hRes = resolution.vRes = resolutions.last(); + else + resolution.hRes = resolution.vRes = resolutions.first(); + if (resolution.hRes == 0) + resolution.hRes = resolution.vRes = 600; + } else { + resolution.hRes = resolution.vRes = qt_defaultDpi(); + } + + setPageSize(m_pageLayout.pageSize()); + + QHash::const_iterator propC; + for (propC = valueCache.constBegin(); propC != valueCache.constEnd(); ++propC) { + q->setProperty(propC.key(), propC.value()); + } +} + +void QMacPrintEnginePrivate::releaseSession() +{ + PMSessionEndPageNoDialog(session()); + PMSessionEndDocumentNoDialog(session()); + [printInfo release]; + printInfo = nil; +} + +bool QMacPrintEnginePrivate::newPage_helper() +{ + Q_Q(QMacPrintEngine); + Q_ASSERT(state == QPrinter::Active); + + if (PMSessionError(session()) != noErr) { + q->abort(); + return false; + } + + // pop the stack of saved graphic states, in case we get the same + // context back - either way, the stack count should be 0 when we + // get the new one + QCoreGraphicsPaintEngine *cgEngine = static_cast(paintEngine); + while (cgEngine->d_func()->stackCount > 0) + cgEngine->d_func()->restoreGraphicsState(); + + OSStatus status = PMSessionBeginPageNoDialog(session(), format(), nullptr); + if (status != noErr) { + state = QPrinter::Error; + return false; + } + + QRect page = m_pageLayout.paintRectPixels(resolution.hRes); + QRect paper = m_pageLayout.fullRectPixels(resolution.hRes); + + CGContextRef cgContext; + OSStatus err = noErr; + err = PMSessionGetCGGraphicsContext(session(), &cgContext); + if (err != noErr) { + qWarning("QMacPrintEngine::newPage: Cannot retrieve CoreGraphics context: %ld", long(err)); + state = QPrinter::Error; + return false; + } + cgEngine->d_func()->hd = cgContext; + + // Set the resolution as a scaling ration of 72 (the default). + CGContextScaleCTM(cgContext, 72 / resolution.hRes, 72 / resolution.vRes); + + CGContextScaleCTM(cgContext, 1, -1); + CGContextTranslateCTM(cgContext, 0, -paper.height()); + if (m_pageLayout.mode() != QPageLayout::FullPageMode) + CGContextTranslateCTM(cgContext, page.x() - paper.x(), page.y() - paper.y()); + cgEngine->d_func()->orig_xform = CGContextGetCTM(cgContext); + cgEngine->d_func()->setClip(nullptr); + cgEngine->state->dirtyFlags = QPaintEngine::DirtyFlag(QPaintEngine::AllDirty + & ~(QPaintEngine::DirtyClipEnabled + | QPaintEngine::DirtyClipRegion + | QPaintEngine::DirtyClipPath)); + if (cgEngine->painter()->hasClipping()) + cgEngine->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled; + cgEngine->syncState(); + return true; +} + +void QMacPrintEnginePrivate::setPageSize(const QPageSize &pageSize) +{ + if (!pageSize.isValid()) + return; + + // Get the matching printer paper + QPageSize printerPageSize = m_printDevice->supportedPageSize(pageSize); + QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize; + + // Get the PMPaper and check it is valid + PMPaper macPaper = m_printDevice->macPaper(usePageSize); + if (!macPaper) { + qWarning() << "QMacPrintEngine: Invalid PMPaper returned for " << pageSize; + return; + } + + QMarginsF printable = m_printDevice->printableMargins(usePageSize, m_pageLayout.orientation(), resolution.hRes); + m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units())); + + // You cannot set the page size on a PMPageFormat, you must create a new PMPageFormat + PMPageFormat pageFormat; + PMCreatePageFormatWithPMPaper(&pageFormat, macPaper); + PMSetOrientation(pageFormat, m_pageLayout.orientation() == QPageLayout::Landscape ? kPMLandscape : kPMPortrait, kPMUnlocked); + PMCopyPageFormat(pageFormat, format()); + if (PMSessionValidatePageFormat(session(), format(), kPMDontWantBoolean) != noErr) + qWarning("QMacPrintEngine: Invalid page format"); + PMRelease(pageFormat); +} + +void QMacPrintEngine::updateState(const QPaintEngineState &state) +{ + d_func()->paintEngine->updateState(state); +} + +void QMacPrintEngine::drawRects(const QRectF *r, int num) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + d->paintEngine->drawRects(r, num); +} + +void QMacPrintEngine::drawPoints(const QPointF *points, int pointCount) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + d->paintEngine->drawPoints(points, pointCount); +} + +void QMacPrintEngine::drawEllipse(const QRectF &r) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + d->paintEngine->drawEllipse(r); +} + +void QMacPrintEngine::drawLines(const QLineF *lines, int lineCount) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + d->paintEngine->drawLines(lines, lineCount); +} + +void QMacPrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + d->paintEngine->drawPolygon(points, pointCount, mode); +} + +void QMacPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + d->paintEngine->drawPixmap(r, pm, sr); +} + +void QMacPrintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + d->paintEngine->drawImage(r, pm, sr, flags); +} + +void QMacPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + if (!d->embedFonts) + QPaintEngine::drawTextItem(p, ti); + else + d->paintEngine->drawTextItem(p, ti); +} + +void QMacPrintEngine::drawTiledPixmap(const QRectF &dr, const QPixmap &pixmap, const QPointF &sr) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + d->paintEngine->drawTiledPixmap(dr, pixmap, sr); +} + +void QMacPrintEngine::drawPath(const QPainterPath &path) +{ + Q_D(QMacPrintEngine); + Q_ASSERT(d->state == QPrinter::Active); + d->paintEngine->drawPath(path); +} + + +void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value) +{ + Q_D(QMacPrintEngine); + + d->valueCache.insert(key, value); + if (!d->printInfo) + return; + + switch (key) { + + // The following keys are properties or derived values and so cannot be set + case PPK_PageRect: + break; + case PPK_PaperRect: + break; + case PPK_PaperSources: + break; + case PPK_SupportsMultipleCopies: + break; + case PPK_SupportedResolutions: + break; + + // The following keys are settings that are unsupported by the Mac PrintEngine + case PPK_ColorMode: + break; + case PPK_CustomBase: + break; + case PPK_PageOrder: + // TODO Check if can be supported via Cups Options + break; + case PPK_PaperSource: + // TODO Check if can be supported via Cups Options + break; + case PPK_PrinterProgram: + break; + case PPK_SelectionOption: + break; + + // The following keys are properties and settings that are supported by the Mac PrintEngine + case PPK_FontEmbedding: + d->embedFonts = value.toBool(); + break; + case PPK_Resolution: { + int bestResolution = 0; + int dpi = value.toInt(); + int bestDistance = INT_MAX; + for (int resolution : d->m_printDevice->supportedResolutions()) { + if (dpi == resolution) { + bestResolution = resolution; + break; + } else { + int distance = qAbs(dpi - resolution); + if (distance < bestDistance) { + bestDistance = distance; + bestResolution = resolution; + } + } + } + PMResolution resolution; + resolution.hRes = resolution.vRes = bestResolution; + if (PMPrinterSetOutputResolution(d->m_printDevice->macPrinter(), d->settings(), &resolution) == noErr) { + // Setting the resolution succeeded. + // Now try to read the actual resolution selected by the OS. + if (PMPrinterGetOutputResolution(d->m_printDevice->macPrinter(), d->settings(), &d->resolution) != noErr) { + // Reading the resolution somehow failed; d->resolution is in undefined state. + // So use the value which was acceptable to PMPrinterSetOutputResolution. + d->resolution = resolution; + } + } + break; + } + case PPK_CollateCopies: + PMSetCollate(d->settings(), value.toBool()); + break; + case PPK_Creator: + d->m_creator = value.toString(); + break; + case PPK_DocumentName: + PMPrintSettingsSetJobName(d->settings(), QCFString(value.toString())); + break; + case PPK_Duplex: { + QPrint::DuplexMode mode = QPrint::DuplexMode(value.toInt()); + if (mode == property(PPK_Duplex).toInt() || !d->m_printDevice->supportedDuplexModes().contains(mode)) + break; + switch (mode) { + case QPrint::DuplexNone: + PMSetDuplex(d->settings(), kPMDuplexNone); + break; + case QPrint::DuplexAuto: + PMSetDuplex(d->settings(), d->m_pageLayout.orientation() == QPageLayout::Landscape ? kPMDuplexTumble : kPMDuplexNoTumble); + break; + case QPrint::DuplexLongSide: + PMSetDuplex(d->settings(), kPMDuplexNoTumble); + break; + case QPrint::DuplexShortSide: + PMSetDuplex(d->settings(), kPMDuplexTumble); + break; + default: + // Don't change + break; + } + break; + } + case PPK_FullPage: + if (value.toBool()) + d->m_pageLayout.setMode(QPageLayout::FullPageMode); + else + d->m_pageLayout.setMode(QPageLayout::StandardMode); + break; + case PPK_CopyCount: // fallthrough + case PPK_NumberOfCopies: + PMSetCopies(d->settings(), value.toInt(), false); + break; + case PPK_Orientation: { + // First try set the Mac format orientation, then set our orientation to match result + QPageLayout::Orientation newOrientation = QPageLayout::Orientation(value.toInt()); + PMOrientation macOrientation = (newOrientation == QPageLayout::Landscape) ? kPMLandscape : kPMPortrait; + PMSetOrientation(d->format(), macOrientation, kPMUnlocked); + PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean); + PMGetOrientation(d->format(), &macOrientation); + d->m_pageLayout.setOrientation(macOrientation == kPMLandscape ? QPageLayout::Landscape : QPageLayout::Portrait); + break; + } + case PPK_OutputFileName: + d->outputFilename = value.toString(); + break; + case PPK_PageSize: + d->setPageSize(QPageSize(QPageSize::PageSizeId(value.toInt()))); + break; + case PPK_PaperName: + // Get the named page size from the printer if supported + d->setPageSize(d->m_printDevice->supportedPageSize(value.toString())); + break; + case PPK_WindowsPageSize: + d->setPageSize(QPageSize(QPageSize::id(value.toInt()))); + break; + case PPK_PrinterName: { + QVariant pageSize = QVariant::fromValue(d->m_pageLayout.pageSize()); + const bool isFullPage = d->m_pageLayout.mode() == QPageLayout::FullPageMode; + QVariant orientation = QVariant::fromValue(d->m_pageLayout.orientation()); + QVariant margins = QVariant::fromValue(QPair(d->m_pageLayout.margins(), + d->m_pageLayout.units())); + QString id = value.toString(); + if (id.isEmpty()) + id = QCocoaPrinterSupport().defaultPrintDeviceId(); + else if (!QCocoaPrinterSupport().availablePrintDeviceIds().contains(id)) + break; + d->m_printDevice.reset(new QCocoaPrintDevice(id)); + PMPrinter printer = d->m_printDevice->macPrinter(); + PMRetain(printer); + PMSessionSetCurrentPMPrinter(d->session(), printer); + // Ensure the settings are up to date and valid + if (d->m_printDevice->supportedPageSize(pageSize.value()).isValid()) + setProperty(PPK_QPageSize, pageSize); + else + setProperty(PPK_CustomPaperSize, pageSize.value().size(QPageSize::Point)); + setProperty(PPK_FullPage, QVariant(isFullPage)); + setProperty(PPK_Orientation, orientation); + setProperty(PPK_QPageMargins, margins); + break; + } + case PPK_CustomPaperSize: + d->setPageSize(QPageSize(value.toSizeF(), QPageSize::Point)); + break; + case PPK_PageMargins: + { + QList margins(value.toList()); + Q_ASSERT(margins.size() == 4); + d->m_pageLayout.setMargins(QMarginsF(margins.at(0).toReal(), margins.at(1).toReal(), + margins.at(2).toReal(), margins.at(3).toReal())); + break; + } + case PPK_QPageSize: + d->setPageSize(value.value()); + break; + case PPK_QPageMargins: { + QPair pair = value.value >(); + d->m_pageLayout.setUnits(pair.second); + d->m_pageLayout.setMargins(pair.first); + break; + } + case PPK_QPageLayout: { + QPageLayout pageLayout = value.value(); + if (pageLayout.isValid() && d->m_printDevice->isValidPageLayout(pageLayout, d->resolution.hRes)) { + setProperty(PPK_QPageSize, QVariant::fromValue(pageLayout.pageSize())); + setProperty(PPK_FullPage, pageLayout.mode() == QPageLayout::FullPageMode); + setProperty(PPK_Orientation, QVariant::fromValue(pageLayout.orientation())); + d->m_pageLayout.setUnits(pageLayout.units()); + d->m_pageLayout.setMargins(pageLayout.margins()); + } + break; + } + // No default so that compiler will complain if new keys added and not handled in this engine + } +} + +QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const +{ + Q_D(const QMacPrintEngine); + QVariant ret; + + if (!d->printInfo && d->valueCache.contains(key)) + return *d->valueCache.find(key); + + switch (key) { + + // The following keys are settings that are unsupported by the Mac PrintEngine + // Return sensible default values to ensure consistent behavior across platforms + case PPK_ColorMode: + ret = QPrinter::Color; + break; + case PPK_CustomBase: + // Special case, leave null + break; + case PPK_PageOrder: + // TODO Check if can be supported via Cups Options + ret = QPrinter::FirstPageFirst; + break; + case PPK_PaperSource: + // TODO Check if can be supported via Cups Options + ret = QPrinter::Auto; + break; + case PPK_PaperSources: { + // TODO Check if can be supported via Cups Options + QList out; + out << int(QPrinter::Auto); + ret = out; + break; + } + case PPK_PrinterProgram: + ret = QString(); + break; + case PPK_SelectionOption: + ret = QString(); + break; + + // The following keys are properties and settings that are supported by the Mac PrintEngine + case PPK_FontEmbedding: + ret = d->embedFonts; + break; + case PPK_CollateCopies: { + Boolean status; + PMGetCollate(d->settings(), &status); + ret = bool(status); + break; + } + case PPK_Creator: + ret = d->m_creator; + break; + case PPK_DocumentName: { + CFStringRef name; + PMPrintSettingsGetJobName(d->settings(), &name); + ret = QString::fromCFString(name); + break; + } + case PPK_Duplex: { + PMDuplexMode mode = kPMDuplexNone; + PMGetDuplex(d->settings(), &mode); + switch (mode) { + case kPMDuplexNoTumble: + ret = QPrinter::DuplexLongSide; + break; + case kPMDuplexTumble: + ret = QPrinter::DuplexShortSide; + break; + case kPMDuplexNone: + default: + ret = QPrinter::DuplexNone; + break; + } + break; + } + case PPK_FullPage: + ret = d->m_pageLayout.mode() == QPageLayout::FullPageMode; + break; + case PPK_NumberOfCopies: + ret = 1; + break; + case PPK_CopyCount: { + UInt32 copies = 1; + PMGetCopies(d->settings(), &copies); + ret = (uint) copies; + break; + } + case PPK_SupportsMultipleCopies: + ret = true; + break; + case PPK_Orientation: + ret = d->m_pageLayout.orientation(); + break; + case PPK_OutputFileName: + ret = d->outputFilename; + break; + case PPK_PageRect: + // PageRect is returned in device pixels + ret = d->m_pageLayout.paintRectPixels(d->resolution.hRes); + break; + case PPK_PageSize: + ret = d->m_pageLayout.pageSize().id(); + break; + case PPK_PaperName: + ret = d->m_pageLayout.pageSize().name(); + break; + case PPK_WindowsPageSize: + ret = d->m_pageLayout.pageSize().windowsId(); + break; + case PPK_PaperRect: + // PaperRect is returned in device pixels + ret = d->m_pageLayout.fullRectPixels(d->resolution.hRes); + break; + case PPK_PrinterName: + return d->m_printDevice->id(); + break; + case PPK_Resolution: { + ret = d->resolution.hRes; + break; + } + case PPK_SupportedResolutions: { + QList list; + for (int resolution : d->m_printDevice->supportedResolutions()) + list << resolution; + ret = list; + break; + } + case PPK_CustomPaperSize: + ret = d->m_pageLayout.fullRectPoints().size(); + break; + case PPK_PageMargins: { + QList list; + QMarginsF margins = d->m_pageLayout.margins(QPageLayout::Point); + list << margins.left() << margins.top() << margins.right() << margins.bottom(); + ret = list; + break; + } + case PPK_QPageSize: + ret.setValue(d->m_pageLayout.pageSize()); + break; + case PPK_QPageMargins: { + QPair pair = qMakePair(d->m_pageLayout.margins(), d->m_pageLayout.units()); + ret.setValue(pair); + break; + } + case PPK_QPageLayout: + ret.setValue(d->m_pageLayout); + // No default so that compiler will complain if new keys added and not handled in this engine + } + return ret; +} + +NSPrintInfo *QMacPrintEngine::printInfo() +{ + Q_D(QMacPrintEngine); + if (d->state == QPrinter::Idle && !d->isPrintSessionInitialized()) + d->initialize(); + return d->printInfo; +} + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER diff --git a/src/printsupport/platform/macos/qprintengine_mac_p.h b/src/printsupport/platform/macos/qprintengine_mac_p.h new file mode 100644 index 0000000000..c76eee4ee7 --- /dev/null +++ b/src/printsupport/platform/macos/qprintengine_mac_p.h @@ -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$ +** +****************************************************************************/ + +#ifndef QPRINTENGINE_MAC_P_H +#define QPRINTENGINE_MAC_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 + +#ifndef QT_NO_PRINTER + +#include +#include +#include +#include + +#include "qcocoaprintdevice_p.h" + +#include "qpaintengine_mac_p.h" + +Q_FORWARD_DECLARE_OBJC_CLASS(NSPrintInfo); + +QT_BEGIN_NAMESPACE + +class QPrinterPrivate; +class QMacPrintEnginePrivate; +class QMacPrintEngine : public QPaintEngine, public QPrintEngine +{ + Q_DECLARE_PRIVATE(QMacPrintEngine) +public: + QMacPrintEngine(QPrinter::PrinterMode mode, const QString &deviceId); + + Qt::HANDLE handle() const; + + bool begin(QPaintDevice *dev); + bool end(); + virtual QPaintEngine::Type type() const { return QPaintEngine::MacPrinter; } + + QPaintEngine *paintEngine() const; + + void setProperty(PrintEnginePropertyKey key, const QVariant &value); + QVariant property(PrintEnginePropertyKey key) const; + + QPrinter::PrinterState printerState() const; + + bool newPage(); + bool abort(); + int metric(QPaintDevice::PaintDeviceMetric) const; + + NSPrintInfo *printInfo(); + + //forwarded functions + + void updateState(const QPaintEngineState &state); + + virtual void drawLines(const QLineF *lines, int lineCount); + virtual void drawRects(const QRectF *r, int num); + virtual void drawPoints(const QPointF *p, int pointCount); + virtual void drawEllipse(const QRectF &r); + virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); + virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags); + virtual void drawTextItem(const QPointF &p, const QTextItem &ti); + virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); + virtual void drawPath(const QPainterPath &); + +private: + friend class QCocoaNativeInterface; +}; + +class QMacPrintEnginePrivate : public QPaintEnginePrivate +{ + Q_DECLARE_PUBLIC(QMacPrintEngine) +public: + QPrinter::PrinterMode mode; + QPrinter::PrinterState state; + QSharedPointer m_printDevice; + QPageLayout m_pageLayout; + NSPrintInfo *printInfo; + PMResolution resolution; + QString outputFilename; + QString m_creator; + QPaintEngine *paintEngine; + QHash valueCache; + uint embedFonts; + + QMacPrintEnginePrivate() : mode(QPrinter::ScreenResolution), state(QPrinter::Idle), + m_pageLayout(QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0, 0, 0, 0))), + printInfo(nullptr), paintEngine(nullptr), embedFonts(true) {} + ~QMacPrintEnginePrivate(); + + void initialize(); + void releaseSession(); + bool newPage_helper(); + void setPageSize(const QPageSize &pageSize); + inline bool isPrintSessionInitialized() const + { + return printInfo != 0; + } + + PMPageFormat format() const { return static_cast([printInfo PMPageFormat]); } + PMPrintSession session() const { return static_cast([printInfo PMPrintSession]); } + PMPrintSettings settings() const { return static_cast([printInfo PMPrintSettings]); } + + QPaintEngine *aggregateEngine() override { return paintEngine; } + Qt::HANDLE nativeHandle() override { return q_func()->handle(); } +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER + +#endif // QPRINTENGINE_WIN_P_H diff --git a/src/printsupport/printsupport.pro b/src/printsupport/printsupport.pro index 52e99803c3..e64fbb634c 100644 --- a/src/printsupport/printsupport.pro +++ b/src/printsupport/printsupport.pro @@ -11,6 +11,8 @@ include(kernel/kernel.pri) include(widgets/widgets.pri) include(dialogs/dialogs.pri) +macos: include(platform/macos/macos.pri) + MODULE_PLUGIN_TYPES = \ printsupport load(qt_module) -- cgit v1.2.3