summaryrefslogtreecommitdiffstats
path: root/src/printsupport
diff options
context:
space:
mode:
Diffstat (limited to 'src/printsupport')
-rw-r--r--src/printsupport/CMakeLists.txt11
-rw-r--r--src/printsupport/dialogs/qpagesetupdialog_mac.mm10
-rw-r--r--src/printsupport/dialogs/qprintdialog_mac.mm10
-rw-r--r--src/printsupport/platform/macos/macos.pri13
-rw-r--r--src/printsupport/platform/macos/qcocoaprintdevice.mm498
-rw-r--r--src/printsupport/platform/macos/qcocoaprintdevice_p.h126
-rw-r--r--src/printsupport/platform/macos/qcocoaprintersupport.mm113
-rw-r--r--src/printsupport/platform/macos/qcocoaprintersupport_p.h78
-rw-r--r--src/printsupport/platform/macos/qpaintengine_mac.mm1422
-rw-r--r--src/printsupport/platform/macos/qpaintengine_mac_p.h207
-rw-r--r--src/printsupport/platform/macos/qprintengine_mac.mm805
-rw-r--r--src/printsupport/platform/macos/qprintengine_mac_p.h159
-rw-r--r--src/printsupport/printsupport.pro2
13 files changed, 3440 insertions, 14 deletions
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 <qpa/qplatformnativeinterface.h>
#include <QtPrintSupport/qprintengine.h>
+#include <QtPrintSupport/private/qprintengine_mac_p.h>
+
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<NSPrintInfo *>(voidp);
+ printInfo = static_cast<QMacPrintEngine *>(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 <QtPrintSupport/qprintengine.h>
#include <qpa/qplatformprintdevice.h>
+#include <QtPrintSupport/private/qprintengine_mac_p.h>
+
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<NSPrintInfo *>(voidp);
+ printInfo = static_cast<QMacPrintEngine *>(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 <jlayt@kde.org>
+** 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 <ApplicationServices/ApplicationServices.h>
+
+#include "qcocoaprintdevice_p.h"
+
+#if QT_CONFIG(mimetype)
+#include <QtCore/qmimedatabase.h>
+#endif
+#include <qdebug.h>
+
+#include <QtCore/private/qcore_mac_p.h>
+
+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<PMPaper>(const_cast<void *>(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<CFStringRef>(const_cast<void *>(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 <jlayt@kde.org>
+** 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 <qpa/qplatformprintdevice.h>
+
+#ifndef QT_NO_PRINTER
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <QtPrintSupport/qtprintsupportglobal.h>
+
+#include <cups/ppd.h>
+
+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<QString, PMPaper> m_macPapers;
+
+ // PPD File
+ ppd_file_t *m_ppd;
+
+ QMarginsF m_customMargins;
+ mutable QHash<QString, QMarginsF> 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 <AppKit/AppKit.h>
+
+#include "qcocoaprintersupport_p.h"
+
+#ifndef QT_NO_PRINTER
+
+#include <AppKit/AppKit.h>
+
+#include <QtCore/private/qcore_mac_p.h>
+
+#include "qcocoaprintdevice_p.h"
+#include "qprintengine_mac_p.h"
+
+#include <private/qprinterinfo_p.h>
+
+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<QMacPrintEngine *>(printEngine);
+}
+
+QPrintDevice QCocoaPrinterSupport::createPrintDevice(const QString &id)
+{
+ return QPlatformPrinterSupport::createPrintDevice(new QCocoaPrintDevice(id));
+}
+
+QStringList QCocoaPrinterSupport::availablePrintDeviceIds() const
+{
+ QStringList list;
+ QCFType<CFArrayRef> printerList;
+ if (PMServerCreatePrinterList(kPMServerLocal, &printerList) == noErr) {
+ CFIndex count = CFArrayGetCount(printerList);
+ for (CFIndex i = 0; i < count; ++i) {
+ PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i)));
+ list.append(QString::fromCFString(PMPrinterGetID(printer)));
+ }
+ }
+ return list;
+}
+
+QString QCocoaPrinterSupport::defaultPrintDeviceId() const
+{
+ QCFType<CFArrayRef> printerList;
+ if (PMServerCreatePrinterList(kPMServerLocal, &printerList) == noErr) {
+ CFIndex count = CFArrayGetCount(printerList);
+ for (CFIndex i = 0; i < count; ++i) {
+ PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(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 <qpa/qplatformprintersupport.h>
+#ifndef QT_NO_PRINTER
+
+#include <QtPrintSupport/qtprintsupportglobal.h>
+
+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 <AppKit/AppKit.h>
+#include <CoreGraphics/CoreGraphics.h>
+
+#include "qpaintengine_mac_p.h"
+#include "qprintengine_mac_p.h"
+
+#include <qbitmap.h>
+#include <qpaintdevice.h>
+#include <qpainterpath.h>
+#include <qpixmapcache.h>
+#include <private/qpaintengine_raster_p.h>
+#include <qprinter.h>
+#include <qstack.h>
+#include <qwidget.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qcoreapplication.h>
+#include <qmath.h>
+
+#include <qpa/qplatformpixmap.h>
+
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qfontengine_coretext_p.h>
+#include <private/qnumeric_p.h>
+#include <private/qpainter_p.h>
+#include <private/qpainterpath_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qcoregraphics_p.h>
+
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ QCoreGraphicsPaintEngine utility functions
+ *****************************************************************************/
+
+void qt_mac_cgimage_data_free(void *, const void *memoryToFree, size_t)
+{
+ free(const_cast<void *>(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<quint8 *>(malloc(nbytes));
+ quint32 *sptr = reinterpret_cast<quint32 *>(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<CGDataProviderRef> 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<CGColorRef> 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<CGColorSpaceRef> 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<QBrush *>(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<p.elementCount(); ++i) {
+ const QPainterPath::Element &elm = p.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::MoveToElement:
+ if (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<QWidget*>(d->pdev)->windowType() == Qt::Desktop) {
+ // ### need to do [qt_mac_window_for(static_cast<QWidget *>(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; i<rectCount; ++i) {
+ QRectF r = rects[i];
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathAddRect(path, nullptr, qt_mac_compose_rect(r));
+ d->drawPath(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<CGImageRef> 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<CGImageRef> 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<CGImageRef> 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<const QTextItemInt &>(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<QCoreTextFontEngine *>(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<CGFloat> linedashes;
+ if (pen.style() == Qt::CustomDashLine) {
+ QVector<qreal> 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<const QGradient*>(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<void *>(&current.brush),
+ 1, domain, 4, nullptr, &callbacks);
+
+ CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB)
+ if (bs == Qt::LinearGradientPattern) {
+ const QLinearGradient *linearGrad = static_cast<const QLinearGradient *>(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<const QRadialGradient *>(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 <QtPrintSupport/qtprintsupportglobal.h>
+
+#include <QtGui/qpaintengine.h>
+#include <QtGui/private/qpaintengine_p.h>
+#include <QtGui/private/qpolygonclipper_p.h>
+#include <QtGui/private/qfont_p.h>
+#include <QtCore/qhash.h>
+
+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 &region, 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 <AppKit/AppKit.h>
+#include <ApplicationServices/ApplicationServices.h>
+
+#include "qprintengine_mac_p.h"
+#include "qcocoaprintersupport_p.h"
+#include <quuid.h>
+#include <QtGui/qpagelayout.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+
+#include <QtCore/private/qcore_mac_p.h>
+
+#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<QPrinter *>(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<CFURLRef> 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<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->stackCount = 0;
+ static_cast<QCoreGraphicsPaintEngine*>(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<QCoreGraphicsPaintEngine*>(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<int> 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<QMacPrintEngine::PrintEnginePropertyKey, QVariant>::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<QCoreGraphicsPaintEngine*>(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<QMarginsF, QPageLayout::Unit>(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<QPageSize>()).isValid())
+ setProperty(PPK_QPageSize, pageSize);
+ else
+ setProperty(PPK_CustomPaperSize, pageSize.value<QPageSize>().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<QVariant> 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<QPageSize>());
+ break;
+ case PPK_QPageMargins: {
+ QPair<QMarginsF, QPageLayout::Unit> pair = value.value<QPair<QMarginsF, QPageLayout::Unit> >();
+ d->m_pageLayout.setUnits(pair.second);
+ d->m_pageLayout.setMargins(pair.first);
+ break;
+ }
+ case PPK_QPageLayout: {
+ QPageLayout pageLayout = value.value<QPageLayout>();
+ 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<QVariant> 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<QVariant> 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<QVariant> 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<QMarginsF, QPageLayout::Unit> 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 <QtPrintSupport/qtprintsupportglobal.h>
+
+#ifndef QT_NO_PRINTER
+
+#include <QtPrintSupport/qprinter.h>
+#include <QtPrintSupport/qprintengine.h>
+#include <QtGui/private/qpainter_p.h>
+#include <QtGui/qpagelayout.h>
+
+#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<QCocoaPrintDevice> m_printDevice;
+ QPageLayout m_pageLayout;
+ NSPrintInfo *printInfo;
+ PMResolution resolution;
+ QString outputFilename;
+ QString m_creator;
+ QPaintEngine *paintEngine;
+ QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant> 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<PMPageFormat>([printInfo PMPageFormat]); }
+ PMPrintSession session() const { return static_cast<PMPrintSession>([printInfo PMPrintSession]); }
+ PMPrintSettings settings() const { return static_cast<PMPrintSettings>([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)