diff options
Diffstat (limited to 'src/printsupport/platform')
17 files changed, 2914 insertions, 337 deletions
diff --git a/src/printsupport/platform/macos/macos.pri b/src/printsupport/platform/macos/macos.pri deleted file mode 100644 index 06a7c6a683..0000000000 --- a/src/printsupport/platform/macos/macos.pri +++ /dev/null @@ -1,17 +0,0 @@ -SOURCES += \ - $$PWD/qprintengine_mac.mm \ - $$PWD/qpaintengine_mac.mm \ - $$PWD/qcocoaprintdevice.mm - -HEADERS += \ - $$PWD/qcocoaprintersupport_p.h \ - $$PWD/qcocoaprintdevice_p.h \ - $$PWD/qprintengine_mac_p.h \ - $$PWD/qpaintengine_mac_p.h - -# Disable PCH to allow selectively enabling QT_STATICPLUGIN -NO_PCH_SOURCES += $$PWD/qcocoaprintersupport.mm - -LIBS += -framework ApplicationServices -lcups - -OTHER_FILES += cocoa.json diff --git a/src/printsupport/platform/macos/qcocoaprintdevice.mm b/src/printsupport/platform/macos/qcocoaprintdevice.mm index 704418d194..867e5225db 100644 --- a/src/printsupport/platform/macos/qcocoaprintdevice.mm +++ b/src/printsupport/platform/macos/qcocoaprintdevice.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2014 John Layt <jlayt@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <ApplicationServices/ApplicationServices.h> diff --git a/src/printsupport/platform/macos/qcocoaprintdevice_p.h b/src/printsupport/platform/macos/qcocoaprintdevice_p.h index f40a3b2666..f27b544b0f 100644 --- a/src/printsupport/platform/macos/qcocoaprintdevice_p.h +++ b/src/printsupport/platform/macos/qcocoaprintdevice_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2014 John Layt <jlayt@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QCOCOAPRINTDEVICE_H #define QCOCOAPRINTDEVICE_H @@ -52,9 +16,11 @@ // #include <qpa/qplatformprintdevice.h> +#include <private/qglobal_p.h> #ifndef QT_NO_PRINTER +#include <QtCore/qhash.h> #include <ApplicationServices/ApplicationServices.h> #include <QtPrintSupport/qtprintsupportglobal.h> diff --git a/src/printsupport/platform/macos/qcocoaprintersupport.mm b/src/printsupport/platform/macos/qcocoaprintersupport.mm index 64a63241e7..ed03e87f29 100644 --- a/src/printsupport/platform/macos/qcocoaprintersupport.mm +++ b/src/printsupport/platform/macos/qcocoaprintersupport.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <AppKit/AppKit.h> @@ -57,6 +21,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + QCocoaPrinterSupport::QCocoaPrinterSupport() { } @@ -122,16 +88,14 @@ public: QPlatformPrinterSupport *QCocoaPrinterSupportPlugin::create(const QString &key) { - if (key.compare(key, QLatin1String("cocoaprintersupport"), Qt::CaseInsensitive) != 0) + if (key.compare(key, "cocoaprintersupport"_L1, Qt::CaseInsensitive) != 0) return 0; return new QCocoaPrinterSupport(); } -Q_IMPORT_PLUGIN(QCocoaPrinterSupportPlugin) +QT_END_NAMESPACE #include "qcocoaprintersupport.moc" -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 index c12e8c355b..73ebe7c90a 100644 --- a/src/printsupport/platform/macos/qcocoaprintersupport_p.h +++ b/src/printsupport/platform/macos/qcocoaprintersupport_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QCOCOAPRINTERSUPPORT_H #define QCOCOAPRINTERSUPPORT_H @@ -52,6 +16,7 @@ // #include <qpa/qplatformprintersupport.h> +#include <private/qglobal_p.h> #ifndef QT_NO_PRINTER #include <QtPrintSupport/qtprintsupportglobal.h> diff --git a/src/printsupport/platform/macos/qpaintengine_mac.mm b/src/printsupport/platform/macos/qpaintengine_mac.mm index fd82539df6..27274f116e 100644 --- a/src/printsupport/platform/macos/qpaintengine_mac.mm +++ b/src/printsupport/platform/macos/qpaintengine_mac.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <AppKit/AppKit.h> #include <CoreGraphics/CoreGraphics.h> @@ -84,7 +48,7 @@ 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); + image = std::move(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(); @@ -499,7 +463,7 @@ QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state) updateCompositionMode(state.compositionMode()); if (flags & (DirtyPen | DirtyTransform | DirtyHints)) { - if (!qt_pen_is_cosmetic(d->current.pen, state.renderHints())) { + if (!d->current.pen.isCosmetic()) { 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) { diff --git a/src/printsupport/platform/macos/qpaintengine_mac_p.h b/src/printsupport/platform/macos/qpaintengine_mac_p.h index 5ee158e8e5..a619a3a04a 100644 --- a/src/printsupport/platform/macos/qpaintengine_mac_p.h +++ b/src/printsupport/platform/macos/qpaintengine_mac_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPAINTENGINE_MAC_P_H #define QPAINTENGINE_MAC_P_H @@ -55,7 +19,6 @@ #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> diff --git a/src/printsupport/platform/macos/qprintengine_mac.mm b/src/printsupport/platform/macos/qprintengine_mac.mm index 1b06722447..d6eb71f66d 100644 --- a/src/printsupport/platform/macos/qprintengine_mac.mm +++ b/src/printsupport/platform/macos/qprintengine_mac.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <AppKit/AppKit.h> #include <ApplicationServices/ApplicationServices.h> @@ -255,9 +219,9 @@ void QMacPrintEnginePrivate::initialize() 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(); + resolution.hRes = resolution.vRes = resolutions.constLast(); else - resolution.hRes = resolution.vRes = resolutions.first(); + resolution.hRes = resolution.vRes = resolutions.constFirst(); if (resolution.hRes == 0) resolution.hRes = resolution.vRes = 600; } else { @@ -325,10 +289,10 @@ bool QMacPrintEnginePrivate::newPage_helper() 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)); + cgEngine->state->dirtyFlags = QPaintEngine::DirtyFlags(QPaintEngine::AllDirty) + & ~(QPaintEngine::DirtyClipEnabled + | QPaintEngine::DirtyClipRegion + | QPaintEngine::DirtyClipPath); if (cgEngine->painter()->hasClipping()) cgEngine->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled; cgEngine->syncState(); @@ -612,7 +576,8 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va 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())); + margins.at(2).toReal(), margins.at(3).toReal()), + QPageLayout::OutOfBoundsPolicy::Clamp); break; } case PPK_QPageSize: @@ -621,7 +586,7 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va 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); + d->m_pageLayout.setMargins(pair.first, QPageLayout::OutOfBoundsPolicy::Clamp); break; } case PPK_QPageLayout: { @@ -631,7 +596,7 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va 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()); + d->m_pageLayout.setMargins(pageLayout.margins(), QPageLayout::OutOfBoundsPolicy::Clamp); } break; } diff --git a/src/printsupport/platform/macos/qprintengine_mac_p.h b/src/printsupport/platform/macos/qprintengine_mac_p.h index c76eee4ee7..7a0390cf41 100644 --- a/src/printsupport/platform/macos/qprintengine_mac_p.h +++ b/src/printsupport/platform/macos/qprintengine_mac_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPRINTENGINE_MAC_P_H #define QPRINTENGINE_MAC_P_H diff --git a/src/printsupport/platform/windows/qprintengine_win.cpp b/src/printsupport/platform/windows/qprintengine_win.cpp new file mode 100644 index 0000000000..fa8d03a615 --- /dev/null +++ b/src/printsupport/platform/windows/qprintengine_win.cpp @@ -0,0 +1,1833 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtPrintSupport/qtprintsupportglobal.h> + +#ifndef QT_NO_PRINTER + +#include "qprintengine_win_p.h" + +#include <limits.h> + +#include <private/qprinter_p.h> +#include <private/qfont_p.h> +#include <private/qfontengine_p.h> +#include <private/qpainter_p.h> +#if QT_CONFIG(directwrite) +# include <private/qwindowsfontenginedirectwrite_p.h> +#endif + +#include <qpa/qplatformprintplugin.h> +#include <qpa/qplatformprintersupport.h> + +#include <qbitmap.h> +#include <qdebug.h> +#include <qlist.h> +#include <qpicture.h> +#include <qpa/qplatformpixmap.h> +#include <private/qpicture_p.h> +#include <private/qpixmap_raster_p.h> +#include <QtCore/QMetaType> +#include <QtCore/qt_windows.h> +#include <QtGui/qpagelayout.h> +#include <QtGui/private/qpixmap_win_p.h> + +QT_BEGIN_NAMESPACE + +extern QPainterPath qt_regionToPath(const QRegion ®ion); +extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits); + +// #define QT_DEBUG_DRAW +// #define QT_DEBUG_METRICS + +static void draw_text_item_win(const QPointF &_pos, const QTextItemInt &ti, HDC hdc, + const QTransform &xform, const QPointF &topLeft); + +QWin32PrintEngine::QWin32PrintEngine(QPrinter::PrinterMode mode, const QString &deviceId) + : QAlphaPaintEngine(*(new QWin32PrintEnginePrivate), + PaintEngineFeatures(PrimitiveTransform + | PixmapTransform + | PerspectiveTransform + | PainterPaths + | Antialiasing + | PaintOutsidePaintEvent)) +{ + Q_D(QWin32PrintEngine); + d->mode = mode; + QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get(); + if (ps) + d->m_printDevice = ps->createPrintDevice(deviceId.isEmpty() ? ps->defaultPrintDeviceId() : deviceId); + d->m_pageLayout.setPageSize(d->m_printDevice.defaultPageSize()); + d->initialize(); +} + +static QByteArray msgBeginFailed(const char *function, const DOCINFO &d) +{ + QString result; + QTextStream str(&result); + str << "QWin32PrintEngine::begin: " << function << " failed"; + if (d.lpszDocName && d.lpszDocName[0]) + str << ", document \"" << QString::fromWCharArray(d.lpszDocName) << '"'; + if (d.lpszOutput && d.lpszOutput[0]) + str << ", file \"" << QString::fromWCharArray(d.lpszOutput) << '"'; + return std::move(result).toLocal8Bit(); +} + +bool QWin32PrintEngine::begin(QPaintDevice *pdev) +{ + Q_D(QWin32PrintEngine); + + QAlphaPaintEngine::begin(pdev); + if (!continueCall()) + return true; + + if (d->reinit) { + d->resetDC(); + d->reinit = false; + } + + // ### set default colors and stuff... + + bool ok = d->state == QPrinter::Idle; + + if (!d->hdc) + return false; + + d->devMode->dmCopies = d->num_copies; + + DOCINFO di; + memset(&di, 0, sizeof(DOCINFO)); + di.cbSize = sizeof(DOCINFO); + if (d->docName.isEmpty()) + di.lpszDocName = L"document1"; + else + di.lpszDocName = reinterpret_cast<const wchar_t *>(d->docName.utf16()); + if (d->printToFile && !d->fileName.isEmpty()) + di.lpszOutput = reinterpret_cast<const wchar_t *>(d->fileName.utf16()); + if (d->printToFile) + di.lpszOutput = d->fileName.isEmpty() ? L"FILE:" : reinterpret_cast<const wchar_t *>(d->fileName.utf16()); + if (ok && StartDoc(d->hdc, &di) == SP_ERROR) { + qErrnoWarning(msgBeginFailed("StartDoc", di)); + ok = false; + } + + if (StartPage(d->hdc) <= 0) { + qErrnoWarning(msgBeginFailed("StartPage", di)); + ok = false; + } + + if (!ok) { + d->state = QPrinter::Idle; + } else { + d->state = QPrinter::Active; + } + + d->matrix = QTransform(); + d->has_pen = true; + d->pen = QColor(Qt::black); + d->has_brush = false; + + d->complex_xform = false; + + updateMatrix(d->matrix); + + if (!ok) + cleanUp(); + +#ifdef QT_DEBUG_METRICS + qDebug("QWin32PrintEngine::begin()"); + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + + return ok; +} + +bool QWin32PrintEngine::end() +{ + Q_D(QWin32PrintEngine); + + if (d->hdc) { + if (d->state == QPrinter::Aborted) { + cleanUp(); + AbortDoc(d->hdc); + return true; + } + } + + QAlphaPaintEngine::end(); + if (!continueCall()) + return true; + + if (d->hdc) { + if (EndPage(d->hdc) <= 0) // end; printing done + qErrnoWarning("QWin32PrintEngine::end: EndPage failed (%p)", d->hdc); + if (EndDoc(d->hdc) <= 0) + qErrnoWarning("QWin32PrintEngine::end: EndDoc failed"); + } + + d->state = QPrinter::Idle; + d->reinit = true; + return true; +} + +bool QWin32PrintEngine::newPage() +{ + Q_D(QWin32PrintEngine); + Q_ASSERT(isActive()); + + Q_ASSERT(d->hdc); + + flushAndInit(); + + bool transparent = GetBkMode(d->hdc) == TRANSPARENT; + + if (EndPage(d->hdc) <= 0) { + qErrnoWarning("QWin32PrintEngine::newPage: EndPage failed"); + return false; + } + + if (d->reinit) { + if (!d->resetDC()) + return false; + d->reinit = false; + } + + if (StartPage(d->hdc) <= 0) { + qErrnoWarning("Win32PrintEngine::newPage: StartPage failed"); + return false; + } + + SetTextAlign(d->hdc, TA_BASELINE); + if (transparent) + SetBkMode(d->hdc, TRANSPARENT); + +#ifdef QT_DEBUG_METRICS + qDebug("QWin32PrintEngine::newPage()"); + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + + // ### + return true; + + bool success = false; + if (d->hdc && d->state == QPrinter::Active) { + if (EndPage(d->hdc) > 0) { + // reinitialize the DC before StartPage if needed, + // because resetdc is disabled between calls to the StartPage and EndPage functions + // (see StartPage documentation in the Platform SDK:Windows GDI) +// state = PST_ACTIVEDOC; +// reinit(); +// state = PST_ACTIVE; + // start the new page now + if (d->reinit) { + if (!d->resetDC()) + qErrnoWarning("QWin32PrintEngine::newPage(), ResetDC failed (2)"); + d->reinit = false; + } + success = (StartPage(d->hdc) > 0); + if (!success) + qErrnoWarning("Win32PrintEngine::newPage: StartPage failed (2)"); + } + if (!success) { + d->state = QPrinter::Aborted; + return false; + } + } + return true; +} + +bool QWin32PrintEngine::abort() +{ + // do nothing loop. + return false; +} + +void QWin32PrintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) +{ + Q_D(const QWin32PrintEngine); + + QAlphaPaintEngine::drawTextItem(p, textItem); + if (!continueCall()) + return; + + const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); + QRgb brushColor = state->pen().brush().color().rgb(); + bool fallBack = state->pen().brush().style() != Qt::SolidPattern + || qAlpha(brushColor) != 0xff + || d->txop >= QTransform::TxProject + || !d->embed_fonts; + + if (!fallBack) { + bool deleteFont = false; + HFONT hfont = nullptr; + if (ti.fontEngine->type() == QFontEngine::Win) { + hfont = static_cast<HFONT>(ti.fontEngine->handle()); + } +#if QT_CONFIG(directwrite) + else if (ti.fontEngine->type() == QFontEngine::DirectWrite) { + QWindowsFontEngineDirectWrite *fedw = static_cast<QWindowsFontEngineDirectWrite *>(ti.fontEngine); + hfont = fedw->createHFONT(); + if (hfont) + deleteFont = true; + } +#endif + + if (hfont) { + // Try selecting the font to see if we get a substitution font + SelectObject(d->hdc, hfont); + if (GetDeviceCaps(d->hdc, TECHNOLOGY) != DT_CHARSTREAM) { + LOGFONT logFont; + GetObject(hfont, sizeof(LOGFONT), &logFont); + + wchar_t n[64]; + GetTextFace(d->hdc, 64, n); + fallBack = QString::fromWCharArray(n) + != QString::fromWCharArray(logFont.lfFaceName); + + if (deleteFont) + DeleteObject(hfont); + } + } else { + fallBack = true; + } + } + + + if (fallBack) { + QPaintEngine::drawTextItem(p, textItem); + return ; + } + + COLORREF cf = RGB(qRed(brushColor), qGreen(brushColor), qBlue(brushColor)); + SelectObject(d->hdc, CreateSolidBrush(cf)); + SelectObject(d->hdc, CreatePen(PS_SOLID, 1, cf)); + SetTextColor(d->hdc, cf); + + draw_text_item_win(p, ti, d->hdc, d->matrix, QPointF(0.0, 0.0)); + DeleteObject(SelectObject(d->hdc,GetStockObject(HOLLOW_BRUSH))); + DeleteObject(SelectObject(d->hdc,GetStockObject(BLACK_PEN))); +} + +int QWin32PrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const +{ + Q_D(const QWin32PrintEngine); + + if (!d->hdc) + return 0; + + int val; + int res = d->resolution; + + switch (m) { + case QPaintDevice::PdmWidth: + val = d->m_paintRectPixels.width(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::metric(PdmWidth) = " << val; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + case QPaintDevice::PdmHeight: + val = d->m_paintRectPixels.height(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::metric(PdmHeight) = " << val; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + case QPaintDevice::PdmDpiX: + val = res; + break; + case QPaintDevice::PdmDpiY: + val = res; + break; + case QPaintDevice::PdmPhysicalDpiX: + val = GetDeviceCaps(d->hdc, LOGPIXELSX); + break; + case QPaintDevice::PdmPhysicalDpiY: + val = GetDeviceCaps(d->hdc, LOGPIXELSY); + break; + case QPaintDevice::PdmWidthMM: + val = d->m_paintSizeMM.width(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::metric(PdmWidthMM) = " << val; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + case QPaintDevice::PdmHeightMM: + val = d->m_paintSizeMM.height(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::metric(PdmHeightMM) = " << val; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + case QPaintDevice::PdmNumColors: + { + int bpp = GetDeviceCaps(d->hdc, BITSPIXEL); + if (bpp==32) + val = INT_MAX; + else if (bpp<=8) + val = GetDeviceCaps(d->hdc, NUMCOLORS); + else + val = 1 << (bpp * GetDeviceCaps(d->hdc, PLANES)); + } + break; + case QPaintDevice::PdmDepth: + val = GetDeviceCaps(d->hdc, PLANES); + break; + case QPaintDevice::PdmDevicePixelRatio: + val = 1; + break; + case QPaintDevice::PdmDevicePixelRatioScaled: + val = 1 * QPaintDevice::devicePixelRatioFScale(); + break; + default: + qWarning("QPrinter::metric: Invalid metric command"); + return 0; + } + return val; +} + +void QWin32PrintEngine::updateState(const QPaintEngineState &state) +{ + Q_D(QWin32PrintEngine); + + QAlphaPaintEngine::updateState(state); + if (!continueCall()) + return; + + if (state.state() & DirtyTransform) { + updateMatrix(state.transform()); + } + + if (state.state() & DirtyPen) { + d->pen = state.pen(); + d->has_pen = d->pen.style() != Qt::NoPen && d->pen.isSolid(); + } + + if (state.state() & DirtyBrush) { + QBrush brush = state.brush(); + d->has_brush = brush.style() == Qt::SolidPattern; + d->brush_color = brush.color(); + } + + if (state.state() & DirtyClipEnabled) { + if (state.isClipEnabled()) + updateClipPath(painter()->clipPath(), Qt::ReplaceClip); + else + updateClipPath(QPainterPath(), Qt::NoClip); + } + + if (state.state() & DirtyClipPath) { + updateClipPath(state.clipPath(), state.clipOperation()); + } + + if (state.state() & DirtyClipRegion) { + QRegion clipRegion = state.clipRegion(); + QPainterPath clipPath = qt_regionToPath(clipRegion); + updateClipPath(clipPath, state.clipOperation()); + } +} + +void QWin32PrintEngine::updateClipPath(const QPainterPath &clipPath, Qt::ClipOperation op) +{ + Q_D(QWin32PrintEngine); + + bool doclip = true; + if (op == Qt::NoClip) { + SelectClipRgn(d->hdc, nullptr); + doclip = false; + } + + if (doclip) { + QPainterPath xformed = clipPath * d->matrix; + + if (xformed.isEmpty()) { +// QRegion empty(-0x1000000, -0x1000000, 1, 1); + HRGN empty = CreateRectRgn(-0x1000000, -0x1000000, -0x0fffffff, -0x0ffffff); + SelectClipRgn(d->hdc, empty); + DeleteObject(empty); + } else { + d->composeGdiPath(xformed); + const int ops[] = { + -1, // Qt::NoClip, covered above + RGN_COPY, // Qt::ReplaceClip + RGN_AND, // Qt::IntersectClip + RGN_OR // Qt::UniteClip + }; + Q_ASSERT(op > 0 && unsigned(op) <= sizeof(ops) / sizeof(int)); + SelectClipPath(d->hdc, ops[op]); + } + } + + QPainterPath aclip = qt_regionToPath(alphaClipping()); + if (!aclip.isEmpty()) { + QTransform tx(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y); + d->composeGdiPath(tx.map(aclip)); + SelectClipPath(d->hdc, RGN_DIFF); + } +} + +void QWin32PrintEngine::updateMatrix(const QTransform &m) +{ + Q_D(QWin32PrintEngine); + + QTransform stretch(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y); + d->painterMatrix = m; + d->matrix = d->painterMatrix * stretch; + d->txop = d->matrix.type(); + d->complex_xform = (d->txop > QTransform::TxScale); +} + +enum HBitmapFormat +{ + HBitmapNoAlpha, + HBitmapPremultipliedAlpha, + HBitmapAlpha +}; + +void QWin32PrintEngine::drawPixmap(const QRectF &targetRect, + const QPixmap &originalPixmap, + const QRectF &sourceRect) +{ + Q_D(QWin32PrintEngine); + + QAlphaPaintEngine::drawPixmap(targetRect, originalPixmap, sourceRect); + if (!continueCall()) + return; + + const int tileSize = 2048; + + QRectF r = targetRect; + QRectF sr = sourceRect; + + QPixmap pixmap = originalPixmap; + if (sr.size() != pixmap.size()) { + pixmap = pixmap.copy(sr.toRect()); + } + + qreal scaleX = 1.0f; + qreal scaleY = 1.0f; + + QTransform scaleMatrix = QTransform::fromScale(r.width() / pixmap.width(), r.height() / pixmap.height()); + QTransform adapted = QPixmap::trueMatrix(d->painterMatrix * scaleMatrix, + pixmap.width(), pixmap.height()); + + qreal xform_offset_x = adapted.dx(); + qreal xform_offset_y = adapted.dy(); + + if (d->complex_xform) { + pixmap = pixmap.transformed(adapted); + scaleX = d->stretch_x; + scaleY = d->stretch_y; + } else { + scaleX = d->stretch_x * (r.width() / pixmap.width()) * d->painterMatrix.m11(); + scaleY = d->stretch_y * (r.height() / pixmap.height()) * d->painterMatrix.m22(); + } + + QPointF topLeft = r.topLeft() * d->painterMatrix; + int tx = int(topLeft.x() * d->stretch_x + d->origin_x); + int ty = int(topLeft.y() * d->stretch_y + d->origin_y); + int tw = qAbs(int(pixmap.width() * scaleX)); + int th = qAbs(int(pixmap.height() * scaleY)); + + xform_offset_x *= d->stretch_x; + xform_offset_y *= d->stretch_y; + + int dc_state = SaveDC(d->hdc); + + int tilesw = pixmap.width() / tileSize; + int tilesh = pixmap.height() / tileSize; + ++tilesw; + ++tilesh; + + int txinc = tileSize*scaleX; + int tyinc = tileSize*scaleY; + + for (int y = 0; y < tilesh; ++y) { + int tposy = ty + (y * tyinc); + int imgh = tileSize; + int height = tyinc; + if (y == (tilesh - 1)) { + imgh = pixmap.height() - (y * tileSize); + height = (th - (y * tyinc)); + } + for (int x = 0; x < tilesw; ++x) { + int tposx = tx + (x * txinc); + int imgw = tileSize; + int width = txinc; + if (x == (tilesw - 1)) { + imgw = pixmap.width() - (x * tileSize); + width = (tw - (x * txinc)); + } + + + QImage img(QSize(imgw, imgh), QImage::Format_RGB32); + img.setDevicePixelRatio(pixmap.devicePixelRatio()); + img.fill(Qt::white); + QPainter painter(&img); + painter.drawPixmap(0,0, pixmap, tileSize * x, tileSize * y, imgw, imgh); + QPixmap p = QPixmap::fromImage(img); + + HBITMAP hbitmap = qt_pixmapToWinHBITMAP(p, HBitmapNoAlpha); + HDC hbitmap_hdc = CreateCompatibleDC(d->hdc); + HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap); + + if (!StretchBlt(d->hdc, qRound(tposx - xform_offset_x), qRound(tposy - xform_offset_y), width, height, + hbitmap_hdc, 0, 0, p.width(), p.height(), SRCCOPY)) + qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed"); + + SelectObject(hbitmap_hdc, null_bitmap); + DeleteObject(hbitmap); + DeleteDC(hbitmap_hdc); + } + } + + RestoreDC(d->hdc, dc_state); +} + + +void QWin32PrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &pos) +{ + Q_D(QWin32PrintEngine); + + QAlphaPaintEngine::drawTiledPixmap(r, pm, pos); + if (!continueCall()) + return; + + if (d->complex_xform || !pos.isNull()) { + QPaintEngine::drawTiledPixmap(r, pm, pos); + } else { + int dc_state = SaveDC(d->hdc); + + HBITMAP hbitmap = qt_pixmapToWinHBITMAP(pm, HBitmapNoAlpha); + HDC hbitmap_hdc = CreateCompatibleDC(d->hdc); + HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap); + + QRectF trect = d->painterMatrix.mapRect(r); + int tx = int(trect.left() * d->stretch_x + d->origin_x); + int ty = int(trect.top() * d->stretch_y + d->origin_y); + + int xtiles = int(trect.width() / pm.width()) + 1; + int ytiles = int(trect.height() / pm.height()) + 1; + int xinc = int(pm.width() * d->stretch_x); + int yinc = int(pm.height() * d->stretch_y); + + for (int y = 0; y < ytiles; ++y) { + int ity = ty + (yinc * y); + int ith = pm.height(); + if (y == (ytiles - 1)) { + ith = int(trect.height() - (pm.height() * y)); + } + + for (int x = 0; x < xtiles; ++x) { + int itx = tx + (xinc * x); + int itw = pm.width(); + if (x == (xtiles - 1)) { + itw = int(trect.width() - (pm.width() * x)); + } + + if (!StretchBlt(d->hdc, itx, ity, int(itw * d->stretch_x), int(ith * d->stretch_y), + hbitmap_hdc, 0, 0, itw, ith, SRCCOPY)) + qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed"); + + } + } + + SelectObject(hbitmap_hdc, null_bitmap); + DeleteObject(hbitmap); + DeleteDC(hbitmap_hdc); + + RestoreDC(d->hdc, dc_state); + } +} + + +void QWin32PrintEnginePrivate::composeGdiPath(const QPainterPath &path) +{ + if (!BeginPath(hdc)) + qErrnoWarning("QWin32PrintEnginePrivate::drawPath: BeginPath failed"); + + // Drawing the subpaths + int start = -1; + for (int i=0; i<path.elementCount(); ++i) { + const QPainterPath::Element &elm = path.elementAt(i); + switch (elm.type) { + case QPainterPath::MoveToElement: + if (start >= 0 + && path.elementAt(start).x == path.elementAt(i-1).x + && path.elementAt(start).y == path.elementAt(i-1).y) + CloseFigure(hdc); + start = i; + MoveToEx(hdc, qRound(elm.x), qRound(elm.y), 0); + break; + case QPainterPath::LineToElement: + LineTo(hdc, qRound(elm.x), qRound(elm.y)); + break; + case QPainterPath::CurveToElement: { + POINT pts[3] = { + { qRound(elm.x), qRound(elm.y) }, + { qRound(path.elementAt(i+1).x), qRound(path.elementAt(i+1).y) }, + { qRound(path.elementAt(i+2).x), qRound(path.elementAt(i+2).y) } + }; + i+=2; + PolyBezierTo(hdc, pts, 3); + break; + } + default: + qFatal("QWin32PaintEngine::drawPath: Unhandled type: %d", elm.type); + } + } + + if (start >= 0 + && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x + && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y) + CloseFigure(hdc); + + if (!EndPath(hdc)) + qErrnoWarning("QWin32PaintEngine::drawPath: EndPath failed"); + + SetPolyFillMode(hdc, path.fillRule() == Qt::WindingFill ? WINDING : ALTERNATE); +} + + +void QWin32PrintEnginePrivate::fillPath_dev(const QPainterPath &path, const QColor &color) +{ +#ifdef QT_DEBUG_DRAW + qDebug() << " --- QWin32PrintEnginePrivate::fillPath() bound:" << path.boundingRect() << color; +#endif + + composeGdiPath(path); + + HBRUSH brush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue())); + HGDIOBJ old_brush = SelectObject(hdc, brush); + FillPath(hdc); + DeleteObject(SelectObject(hdc, old_brush)); +} + +void QWin32PrintEnginePrivate::strokePath_dev(const QPainterPath &path, const QColor &color, qreal penWidth) +{ + composeGdiPath(path); + LOGBRUSH brush; + brush.lbStyle = BS_SOLID; + brush.lbColor = RGB(color.red(), color.green(), color.blue()); + DWORD capStyle = PS_ENDCAP_SQUARE; + DWORD joinStyle = PS_JOIN_BEVEL; + if (pen.capStyle() == Qt::FlatCap) + capStyle = PS_ENDCAP_FLAT; + else if (pen.capStyle() == Qt::RoundCap) + capStyle = PS_ENDCAP_ROUND; + + if (pen.joinStyle() == Qt::MiterJoin) + joinStyle = PS_JOIN_MITER; + else if (pen.joinStyle() == Qt::RoundJoin) + joinStyle = PS_JOIN_ROUND; + + HPEN pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | capStyle | joinStyle, + (penWidth == 0) ? 1 : penWidth, &brush, 0, nullptr); + + HGDIOBJ old_pen = SelectObject(hdc, pen); + StrokePath(hdc); + DeleteObject(SelectObject(hdc, old_pen)); +} + + +void QWin32PrintEnginePrivate::fillPath(const QPainterPath &path, const QColor &color) +{ + fillPath_dev(path * matrix, color); +} + +void QWin32PrintEnginePrivate::strokePath(const QPainterPath &path, const QColor &color) +{ + QPainterPathStroker stroker; + if (pen.style() == Qt::CustomDashLine) { + stroker.setDashPattern(pen.dashPattern()); + stroker.setDashOffset(pen.dashOffset()); + } else { + stroker.setDashPattern(pen.style()); + } + stroker.setCapStyle(pen.capStyle()); + stroker.setJoinStyle(pen.joinStyle()); + stroker.setMiterLimit(pen.miterLimit()); + + QPainterPath stroke; + qreal width = pen.widthF(); + bool cosmetic = pen.isCosmetic(); + if (pen.style() == Qt::SolidLine && (cosmetic || matrix.type() < QTransform::TxScale)) { + strokePath_dev(path * matrix, color, width); + } else { + stroker.setWidth(width); + if (cosmetic) { + stroke = stroker.createStroke(path * matrix); + } else { + stroke = stroker.createStroke(path) * painterMatrix; + QTransform stretch(stretch_x, 0, 0, stretch_y, origin_x, origin_y); + stroke = stroke * stretch; + } + + if (stroke.isEmpty()) + return; + + fillPath_dev(stroke, color); + } +} + + +void QWin32PrintEngine::drawPath(const QPainterPath &path) +{ +#ifdef QT_DEBUG_DRAW + qDebug() << " - QWin32PrintEngine::drawPath(), bounds: " << path.boundingRect(); +#endif + + Q_D(QWin32PrintEngine); + + QAlphaPaintEngine::drawPath(path); + if (!continueCall()) + return; + + if (d->has_brush) + d->fillPath(path, d->brush_color); + + if (d->has_pen) + d->strokePath(path, d->pen.color()); +} + + +void QWin32PrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) +{ +#ifdef QT_DEBUG_DRAW + qDebug() << " - QWin32PrintEngine::drawPolygon(), pointCount: " << pointCount; +#endif + + QAlphaPaintEngine::drawPolygon(points, pointCount, mode); + if (!continueCall()) + return; + + Q_ASSERT(pointCount > 1); + + QPainterPath path(points[0]); + + for (int i=1; i<pointCount; ++i) { + path.lineTo(points[i]); + } + + Q_D(QWin32PrintEngine); + + bool has_brush = d->has_brush; + + if (mode == PolylineMode) + d->has_brush = false; // No brush for polylines + else + path.closeSubpath(); // polygons are should always be closed. + + drawPath(path); + d->has_brush = has_brush; +} + +QWin32PrintEnginePrivate::~QWin32PrintEnginePrivate() +{ + release(); +} + +void QWin32PrintEnginePrivate::initialize() +{ + release(); + + Q_ASSERT(!hPrinter); + Q_ASSERT(!hdc); + Q_ASSERT(!devMode); + Q_ASSERT(!pInfo); + + if (!m_printDevice.isValid()) + return; + + txop = QTransform::TxNone; + + QString printerName = m_printDevice.id(); + bool ok = OpenPrinter(reinterpret_cast<LPWSTR>(const_cast<ushort *>(printerName.utf16())), + reinterpret_cast<LPHANDLE>(&hPrinter), nullptr); + if (!ok) { + qErrnoWarning("QWin32PrintEngine::initialize: OpenPrinter failed"); + return; + } + + // Fetch the PRINTER_INFO_2 with DEVMODE data containing the + // printer settings. + DWORD infoSize, numBytes; + GetPrinter(hPrinter, 2, nullptr, 0, &infoSize); + hMem = GlobalAlloc(GHND, infoSize); + pInfo = reinterpret_cast<PRINTER_INFO_2*>(GlobalLock(hMem)); + ok = GetPrinter(hPrinter, 2, reinterpret_cast<LPBYTE>(pInfo), infoSize, &numBytes); + + if (!ok) { + qErrnoWarning("QWin32PrintEngine::initialize: GetPrinter failed"); + release(); + return; + } + + devMode = pInfo->pDevMode; + + if (!devMode) { + // pInfo->pDevMode == NULL for some printers and passing NULL + // into CreateDC leads to the printer doing nothing. In addition, + // the framework assumes that devMode isn't NULL, such as in + // QWin32PrintEngine::begin() and QPageSetupDialog::exec() + // Attempt to get the DEVMODE a different way. + + // Allocate the required buffer + auto *lpwPrinterName = reinterpret_cast<LPWSTR>(const_cast<ushort *>(printerName.utf16())); + LONG result = DocumentProperties(nullptr, hPrinter, lpwPrinterName, + nullptr, nullptr, 0); + devMode = reinterpret_cast<DEVMODE *>(malloc(result)); + initializeDevMode(devMode); + ownsDevMode = true; + + // Get the default DevMode + result = DocumentProperties(nullptr, hPrinter, lpwPrinterName, + devMode, nullptr, DM_OUT_BUFFER); + if (result != IDOK) { + qErrnoWarning("QWin32PrintEngine::initialize: Failed to obtain devMode"); + free(devMode); + devMode = nullptr; + ownsDevMode = false; + } + } + + hdc = CreateDC(nullptr, reinterpret_cast<LPCWSTR>(printerName.utf16()), + nullptr, devMode); + + if (!hdc) { + qErrnoWarning("QWin32PrintEngine::initialize: CreateDC failed"); + release(); + return; + } + + Q_ASSERT(hPrinter); + Q_ASSERT(pInfo); + + initHDC(); + + if (devMode) { + num_copies = devMode->dmCopies; + devMode->dmCollate = DMCOLLATE_TRUE; + updatePageLayout(); + } + +#if defined QT_DEBUG_DRAW || defined QT_DEBUG_METRICS + qDebug("QWin32PrintEngine::initialize()"); + debugMetrics(); +#endif // QT_DEBUG_DRAW || QT_DEBUG_METRICS +} + +void QWin32PrintEnginePrivate::initializeDevMode(DEVMODE *devMode) +{ + memset(devMode, 0, sizeof(DEVMODE)); + devMode->dmSize = sizeof(DEVMODE); + devMode->dmSpecVersion = DM_SPECVERSION; +} + +void QWin32PrintEnginePrivate::initHDC() +{ + Q_ASSERT(hdc); + + HDC display_dc = GetDC(nullptr); + dpi_x = GetDeviceCaps(hdc, LOGPIXELSX); + dpi_y = GetDeviceCaps(hdc, LOGPIXELSY); + dpi_display = GetDeviceCaps(display_dc, LOGPIXELSY); + ReleaseDC(nullptr, display_dc); + if (dpi_display == 0) { + qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, " + "might be a driver problem"); + dpi_display = 96; // Reasonable default + } + + switch(mode) { + case QPrinter::ScreenResolution: + resolution = dpi_display; + stretch_x = dpi_x / double(dpi_display); + stretch_y = dpi_y / double(dpi_display); + break; + case QPrinter::PrinterResolution: + case QPrinter::HighResolution: + resolution = dpi_y; + stretch_x = 1; + stretch_y = 1; + break; + default: + break; + } + + updateMetrics(); +} + +void QWin32PrintEnginePrivate::release() +{ + if (globalDevMode) { // Devmode comes from print dialog + GlobalUnlock(globalDevMode); + } else if (hMem) { + GlobalUnlock(hMem); + GlobalFree(hMem); + } + if (hPrinter) + ClosePrinter(hPrinter); + if (hdc) + DeleteDC(hdc); + + // Check if devMode was allocated separately from pInfo / hMem. + if (ownsDevMode) + free(devMode); + + hdc = nullptr; + hPrinter = nullptr; + pInfo = nullptr; + hMem = nullptr; + devMode = nullptr; + ownsDevMode = false; +} + +void QWin32PrintEnginePrivate::doReinit() +{ + if (state == QPrinter::Active) { + reinit = true; + } else { + resetDC(); + reinit = false; + } +} + +bool QWin32PrintEnginePrivate::resetDC() +{ + if (!hdc) { + qWarning("ResetDC() called with null hdc."); + return false; + } + const HDC oldHdc = hdc; + const HDC hdc = ResetDC(oldHdc, devMode); + if (!hdc) { + const int lastError = GetLastError(); + qErrnoWarning(lastError, "ResetDC() on %p failed (%d)", oldHdc, lastError); + } + return hdc != 0; +} + +static int indexOfId(const QList<QPrint::InputSlot> &inputSlots, QPrint::InputSlotId id) +{ + for (int i = 0; i < inputSlots.size(); ++i) { + if (inputSlots.at(i).id == id) + return i; + } + return -1; +} + +static int indexOfWindowsId(const QList<QPrint::InputSlot> &inputSlots, int windowsId) +{ + for (int i = 0; i < inputSlots.size(); ++i) { + if (inputSlots.at(i).windowsId == windowsId) + return i; + } + return -1; +} + +void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value) +{ + Q_D(QWin32PrintEngine); + 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 Windows PrintEngine + case PPK_CustomBase: + break; + case PPK_PageOrder: + break; + case PPK_PrinterProgram: + break; + case PPK_SelectionOption: + break; + + // The following keys are properties and settings that are supported by the Windows PrintEngine + case PPK_FontEmbedding: + d->embed_fonts = value.toBool(); + break; + + case PPK_CollateCopies: + { + if (!d->devMode) + break; + d->devMode->dmCollate = value.toBool() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE; + d->devMode->dmFields |= DM_COLLATE; + d->doReinit(); + } + break; + + case PPK_ColorMode: + { + if (!d->devMode) + break; + d->devMode->dmColor = (value.toInt() == QPrinter::Color) ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME; + d->devMode->dmFields |= DM_COLOR; + d->doReinit(); + } + break; + + case PPK_Creator: + d->m_creator = value.toString(); + break; + + case PPK_DocumentName: + if (isActive()) { + qWarning("QWin32PrintEngine: Cannot change document name while printing is active"); + return; + } + d->docName = value.toString(); + break; + + case PPK_Duplex: { + if (!d->devMode) + break; + 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: + d->devMode->dmDuplex = DMDUP_SIMPLEX; + d->devMode->dmFields |= DM_DUPLEX; + break; + case QPrint::DuplexAuto: + d->devMode->dmDuplex = d->m_pageLayout.orientation() == QPageLayout::Landscape ? DMDUP_HORIZONTAL : DMDUP_VERTICAL; + d->devMode->dmFields |= DM_DUPLEX; + break; + case QPrint::DuplexLongSide: + d->devMode->dmDuplex = DMDUP_VERTICAL; + d->devMode->dmFields |= DM_DUPLEX; + break; + case QPrint::DuplexShortSide: + d->devMode->dmDuplex = DMDUP_HORIZONTAL; + d->devMode->dmFields |= DM_DUPLEX; + break; + default: + // Don't change + break; + } + d->doReinit(); + break; + } + + case PPK_FullPage: + if (value.toBool()) + d->m_pageLayout.setMode(QPageLayout::FullPageMode); + else + d->m_pageLayout.setMode(QPageLayout::StandardMode); + d->updateMetrics(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_FullPage," << value.toBool() << + ")"; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + + case PPK_CopyCount: + case PPK_NumberOfCopies: + if (!d->devMode) + break; + d->num_copies = value.toInt(); + d->devMode->dmCopies = d->num_copies; + d->devMode->dmFields |= DM_COPIES; + d->doReinit(); + break; + + case PPK_Orientation: { + if (!d->devMode) + break; + QPageLayout::Orientation orientation = QPageLayout::Orientation(value.toInt()); + d->devMode->dmOrientation = orientation == QPageLayout::Landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT; + d->devMode->dmFields |= DM_ORIENTATION; + d->m_pageLayout.setOrientation(orientation); + d->doReinit(); + d->updateMetrics(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_Orientation," << orientation << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + } + + case PPK_OutputFileName: + if (isActive()) { + qWarning("QWin32PrintEngine: Cannot change filename while printing"); + } else { + d->fileName = value.toString(); + d->printToFile = !value.toString().isEmpty(); + } + break; + + case PPK_PageSize: { + if (!d->devMode) + break; + const QPageSize pageSize = QPageSize(QPageSize::PageSizeId(value.toInt())); + if (pageSize.isValid()) { + d->setPageSize(pageSize); + d->doReinit(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_PageSize," << value.toInt() << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + } + break; + } + + case PPK_PaperName: { + if (!d->devMode) + break; + // Get the named page size from the printer if supported + const QPageSize pageSize = d->m_printDevice.supportedPageSize(value.toString()); + if (pageSize.isValid()) { + d->setPageSize(pageSize); + d->doReinit(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_PaperName," << value.toString() << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + } + break; + } + + case PPK_PaperSource: { + if (!d->devMode) + break; + const auto inputSlots = d->m_printDevice.supportedInputSlots(); + const int paperSource = value.toInt(); + const int index = paperSource >= DMBIN_USER ? + indexOfWindowsId(inputSlots, paperSource) : indexOfId(inputSlots, QPrint::InputSlotId(paperSource)); + d->devMode->dmDefaultSource = index >= 0 ? inputSlots.at(index).windowsId : DMBIN_AUTO; + d->doReinit(); + break; + } + + case PPK_PrinterName: { + QString id = value.toString(); + QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get(); + if (!ps) + return; + + 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())); + QPrintDevice printDevice = ps->createPrintDevice(id.isEmpty() ? ps->defaultPrintDeviceId() : id); + if (printDevice.isValid()) { + d->m_printDevice = printDevice; + d->initialize(); + 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_Resolution: { + d->resolution = value.toInt(); + d->stretch_x = d->dpi_x / double(d->resolution); + d->stretch_y = d->dpi_y / double(d->resolution); + d->updateMetrics(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_Resolution," << value.toInt() << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + } + + case PPK_WindowsPageSize: { + if (!d->devMode) + break; + const QPageSize pageSize = QPageSize(QPageSize::id(value.toInt())); + if (pageSize.isValid()) { + d->setPageSize(pageSize); + d->doReinit(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_WindowsPageSize," << value.toInt() << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + } + break; + } + + case PPK_CustomPaperSize: { + if (!d->devMode) + break; + const QPageSize pageSize = QPageSize(value.toSizeF(), QPageSize::Point); + if (pageSize.isValid()) { + d->setPageSize(pageSize); + d->doReinit(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_CustomPaperSize," << value.toSizeF() << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + } + break; + } + + case PPK_PageMargins: { + QList<QVariant> margins(value.toList()); + Q_ASSERT(margins.size() == 4); + d->m_pageLayout.setUnits(QPageLayout::Point); + d->m_pageLayout.setMargins(QMarginsF(margins.at(0).toReal(), margins.at(1).toReal(), + margins.at(2).toReal(), margins.at(3).toReal()), + QPageLayout::OutOfBoundsPolicy::Clamp); + d->updateMetrics(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_PageMargins," << margins << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + } + + case PPK_QPageSize: { + if (!d->devMode) + break; + // Get the page size from the printer if supported + const QPageSize pageSize = value.value<QPageSize>(); + if (pageSize.isValid()) { + d->setPageSize(pageSize); + d->doReinit(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_QPageSize," << pageSize << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + } + 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, QPageLayout::OutOfBoundsPolicy::Clamp); + d->updateMetrics(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_QPageMargins," << pair.first << pair.second << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + break; + } + + case PPK_QPageLayout: { + QPageLayout pageLayout = value.value<QPageLayout>(); + if (pageLayout.isValid() && d->m_printDevice.isValidPageLayout(pageLayout, d->resolution)) { + 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(), QPageLayout::OutOfBoundsPolicy::Clamp); + d->updateMetrics(); +#ifdef QT_DEBUG_METRICS + qDebug() << "QWin32PrintEngine::setProperty(PPK_QPageLayout," << pageLayout << ')'; + d->debugMetrics(); +#endif // QT_DEBUG_METRICS + } + break; + } + + // No default so that compiler will complain if new keys added and not handled in this engine + } +} + +QVariant QWin32PrintEngine::property(PrintEnginePropertyKey key) const +{ + Q_D(const QWin32PrintEngine); + QVariant value; + switch (key) { + + // The following keys are settings that are unsupported by the Windows PrintEngine + // Return sensible default values to ensure consistent behavior across platforms + case PPK_PageOrder: + value = QPrinter::FirstPageFirst; + break; + case PPK_PrinterProgram: + value = QString(); + break; + case PPK_SelectionOption: + value = QString(); + break; + + // The following keys are properties and settings that are supported by the Windows PrintEngine + case PPK_FontEmbedding: + value = d->embed_fonts; + break; + + case PPK_CollateCopies: + if (!d->devMode) + value = false; + else + value = d->devMode->dmCollate == DMCOLLATE_TRUE; + break; + + case PPK_ColorMode: + { + if (!d->devMode) { + value = QPrinter::Color; + } else { + value = (d->devMode->dmColor == DMCOLOR_COLOR) ? QPrinter::Color : QPrinter::GrayScale; + } + } + break; + + case PPK_Creator: + value = d->m_creator; + break; + + case PPK_DocumentName: + value = d->docName; + break; + + case PPK_Duplex: { + if (!d->devMode) { + value = QPrinter::DuplexNone; + } else { + switch (d->devMode->dmDuplex) { + case DMDUP_VERTICAL: + value = QPrinter::DuplexLongSide; + break; + case DMDUP_HORIZONTAL: + value = QPrinter::DuplexShortSide; + break; + case DMDUP_SIMPLEX: + default: + value = QPrinter::DuplexNone; + break; + } + } + break; + } + + case PPK_FullPage: + value = d->m_pageLayout.mode() == QPageLayout::FullPageMode; + break; + + case PPK_CopyCount: + value = d->num_copies; + break; + + case PPK_SupportsMultipleCopies: + value = true; + break; + + case PPK_NumberOfCopies: + value = 1; + break; + + case PPK_Orientation: + value = d->m_pageLayout.orientation(); + break; + + case PPK_OutputFileName: + value = d->fileName; + break; + + case PPK_PageRect: + // PageRect is returned in device pixels + value = d->m_pageLayout.paintRectPixels(d->resolution); + break; + + case PPK_PageSize: + value = d->m_pageLayout.pageSize().id(); + break; + + case PPK_PaperRect: + // PaperRect is returned in device pixels + value = d->m_pageLayout.fullRectPixels(d->resolution); + break; + + case PPK_PaperName: + value = d->m_pageLayout.pageSize().name(); + break; + + case PPK_PaperSource: + if (!d->devMode) { + value = d->m_printDevice.defaultInputSlot().id; + } else { + if (d->devMode->dmDefaultSource >= DMBIN_USER) { + value = int(d->devMode->dmDefaultSource); + } else { + const auto inputSlots = d->m_printDevice.supportedInputSlots(); + const int index = indexOfWindowsId(inputSlots, d->devMode->dmDefaultSource); + value = index >= 0 ? inputSlots.at(index).id : QPrint::Auto; + } + } + break; + + case PPK_PrinterName: + value = d->m_printDevice.id(); + break; + + case PPK_Resolution: + if (d->resolution || d->m_printDevice.isValid()) + value = d->resolution; + break; + + case PPK_SupportedResolutions: { + QList<QVariant> list; + const auto resolutions = d->m_printDevice.supportedResolutions(); + list.reserve(resolutions.size()); + for (int resolution : resolutions) + list << resolution; + value = list; + break; + } + + case PPK_WindowsPageSize: + value = d->m_pageLayout.pageSize().windowsId(); + break; + + case PPK_PaperSources: { + QList<QVariant> out; + const auto inputSlots = d->m_printDevice.supportedInputSlots(); + out.reserve(inputSlots.size()); + for (const QPrint::InputSlot &inputSlot : inputSlots) + out << QVariant(inputSlot.id == QPrint::CustomInputSlot ? inputSlot.windowsId : int(inputSlot.id)); + value = out; + break; + } + + case PPK_CustomPaperSize: + value = 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(); + value = list; + break; + } + + case PPK_QPageSize: + value.setValue(d->m_pageLayout.pageSize()); + break; + + case PPK_QPageMargins: { + QPair<QMarginsF, QPageLayout::Unit> pair = qMakePair(d->m_pageLayout.margins(), d->m_pageLayout.units()); + value.setValue(pair); + break; + } + + case PPK_QPageLayout: + value.setValue(d->m_pageLayout); + break; + + case PPK_CustomBase: + break; + + // No default so that compiler will complain if new keys added and not handled in this engine + } + return value; +} + +QPrinter::PrinterState QWin32PrintEngine::printerState() const +{ + return d_func()->state; +} + +HDC QWin32PrintEngine::getDC() const +{ + return d_func()->hdc; +} + +void QWin32PrintEngine::releaseDC(HDC) const +{ + +} + +HGLOBAL *QWin32PrintEngine::createGlobalDevNames() +{ + Q_D(QWin32PrintEngine); + + const size_t size = sizeof(DEVNAMES) + d->m_printDevice.id().length() * 2 + 2; + auto hGlobal = reinterpret_cast<HGLOBAL *>(GlobalAlloc(GMEM_MOVEABLE, size)); + auto dn = reinterpret_cast<DEVNAMES*>(GlobalLock(hGlobal)); + + dn->wDriverOffset = 0; + dn->wDeviceOffset = sizeof(DEVNAMES) / sizeof(wchar_t); + dn->wOutputOffset = 0; + + memcpy(reinterpret_cast<ushort*>(dn) + dn->wDeviceOffset, + d->m_printDevice.id().utf16(), d->m_printDevice.id().length() * 2 + 2); + dn->wDefault = 0; + + GlobalUnlock(hGlobal); + return hGlobal; +} + +void QWin32PrintEngine::setGlobalDevMode(HGLOBAL globalDevNames, HGLOBAL globalDevMode) +{ + Q_D(QWin32PrintEngine); + if (globalDevNames) { + auto dn = reinterpret_cast<DEVNAMES*>(GlobalLock(globalDevNames)); + const QString id = + QString::fromWCharArray(reinterpret_cast<const wchar_t*>(dn) + dn->wDeviceOffset); + QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get(); + if (ps) + d->m_printDevice = ps->createPrintDevice(id.isEmpty() ? ps->defaultPrintDeviceId() : id); + GlobalUnlock(globalDevNames); + } + + if (globalDevMode) { + auto dm = reinterpret_cast<DEVMODE*>(GlobalLock(globalDevMode)); + d->release(); + d->globalDevMode = globalDevMode; + if (d->ownsDevMode) { + free(d->devMode); + d->ownsDevMode = false; + } + d->devMode = dm; + d->hdc = CreateDC(nullptr, reinterpret_cast<LPCWSTR>(d->m_printDevice.id().utf16()), nullptr, dm); + + d->num_copies = d->devMode->dmCopies; + d->updatePageLayout(); + + if (!OpenPrinter((wchar_t*)d->m_printDevice.id().utf16(), &d->hPrinter, 0)) + qWarning("QPrinter: OpenPrinter() failed after reading DEVMODE."); + } + + if (d->hdc) + d->initHDC(); + +#if defined QT_DEBUG_DRAW || defined QT_DEBUG_METRICS + qDebug("QWin32PrintEngine::setGlobalDevMode()"); + d->debugMetrics(); +#endif // QT_DEBUG_DRAW || QT_DEBUG_METRICS +} + +HGLOBAL QWin32PrintEngine::globalDevMode() +{ + Q_D(QWin32PrintEngine); + return d->globalDevMode; +} + +void QWin32PrintEnginePrivate::setPageSize(const QPageSize &pageSize) +{ + if (!pageSize.isValid()) + return; + + Q_ASSERT(devMode); + + // Use the printer page size if supported + const QPageSize printerPageSize = m_printDevice.supportedPageSize(pageSize); + const QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize; + + const QMarginsF printable = m_printDevice.printableMargins(usePageSize, m_pageLayout.orientation(), resolution); + m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units())); + + // Setup if Windows custom size, i.e. not a known Windows ID + if (printerPageSize.isValid()) { + has_custom_paper_size = false; + devMode->dmPaperSize = m_pageLayout.pageSize().windowsId(); + devMode->dmFields &= ~(DM_PAPERLENGTH | DM_PAPERWIDTH); + devMode->dmPaperWidth = 0; + devMode->dmPaperLength = 0; + } else { + devMode->dmPaperSize = DMPAPER_USER; + devMode->dmFields |= DM_PAPERLENGTH | DM_PAPERWIDTH; + // Size in tenths of a millimeter + const QSizeF sizeMM = m_pageLayout.pageSize().size(QPageSize::Millimeter); + devMode->dmPaperWidth = qRound(sizeMM.width() * 10.0); + devMode->dmPaperLength = qRound(sizeMM.height() * 10.0); + } + updateMetrics(); +} + +// Update the page layout after any changes made to devMode +void QWin32PrintEnginePrivate::updatePageLayout() +{ + Q_ASSERT(devMode); + + // Update orientation first as is needed to obtain printable margins when changing page size + m_pageLayout.setOrientation(devMode->dmOrientation == DMORIENT_LANDSCAPE ? QPageLayout::Landscape : QPageLayout::Portrait); + if (devMode->dmPaperSize >= DMPAPER_LAST) { + // Is a custom size + // Check if it is using the Postscript Custom Size first + bool hasCustom = false; + int feature = PSIDENT_GDICENTRIC; + if (ExtEscape(hdc, POSTSCRIPT_IDENTIFY, + sizeof(DWORD), reinterpret_cast<LPCSTR>(&feature), 0, 0) >= 0) { + PSFEATURE_CUSTPAPER custPaper; + feature = FEATURESETTING_CUSTPAPER; + if (ExtEscape(hdc, GET_PS_FEATURESETTING, sizeof(INT), reinterpret_cast<LPCSTR>(&feature), + sizeof(custPaper), reinterpret_cast<LPSTR>(&custPaper)) > 0) { + // If orientation is 1 and width/height is 0 then it's not really custom + if (!(custPaper.lOrientation == 1 && custPaper.lWidth == 0 && custPaper.lHeight == 0)) { + if (custPaper.lOrientation == 0 || custPaper.lOrientation == 2) + m_pageLayout.setOrientation(QPageLayout::Portrait); + else + m_pageLayout.setOrientation(QPageLayout::Landscape); + QPageSize pageSize = QPageSize(QSizeF(custPaper.lWidth, custPaper.lHeight), + QPageSize::Point); + setPageSize(pageSize); + hasCustom = true; + } + } + } + if (!hasCustom) { + QPageSize pageSize = QPageSize(QSizeF(devMode->dmPaperWidth / 10.0f, devMode->dmPaperLength / 10.0f), + QPageSize::Millimeter); + setPageSize(pageSize); + } + } else { + // Is a supported size + setPageSize(QPageSize(QPageSize::id(devMode->dmPaperSize))); + } + updateMetrics(); +} + +// Update the cached page paint metrics whenever page layout is changed +void QWin32PrintEnginePrivate::updateMetrics() +{ + m_paintRectPixels = m_pageLayout.paintRectPixels(resolution); + // Some print devices allow scaling, so that "virtual" page size != current paper size + const int devWidth = GetDeviceCaps(hdc, PHYSICALWIDTH); + const int devHeight = GetDeviceCaps(hdc, PHYSICALHEIGHT); + const int pageWidth = m_pageLayout.fullRectPixels(dpi_x).width(); + const int pageHeight = m_pageLayout.fullRectPixels(dpi_y).height(); + const qreal pageScaleX = (devWidth && pageWidth) ? qreal(devWidth) / pageWidth : 1; + const qreal pageScaleY = (devHeight && pageHeight) ? qreal(devHeight) / pageHeight : 1; + m_paintRectPixels = QTransform::fromScale(pageScaleX, pageScaleY).mapRect(m_paintRectPixels); + + QSizeF sizeMM = m_pageLayout.paintRect(QPageLayout::Millimeter).size(); + m_paintSizeMM = QSize(qRound(sizeMM.width()), qRound(sizeMM.height())); + // Calculate the origin using the physical device pixels, not our paint pixels + // Origin is defined as User Margins - Device Margins + const bool isFullPage = (m_pageLayout.mode() == QPageLayout::FullPageMode); + const QMarginsF margins = isFullPage ? QMarginsF() : (m_pageLayout.margins(QPageLayout::Millimeter) / 25.4); + origin_x = qRound(pageScaleX * margins.left() * dpi_x) - GetDeviceCaps(hdc, PHYSICALOFFSETX); + origin_y = qRound(pageScaleY * margins.top() * dpi_y) - GetDeviceCaps(hdc, PHYSICALOFFSETY); +} + +void QWin32PrintEnginePrivate::debugMetrics() const +{ + qDebug() << " " << "m_pageLayout = " << m_pageLayout; + qDebug() << " " << "m_paintRectPixels = " << m_paintRectPixels; + qDebug() << " " << "m_paintSizeMM = " << m_paintSizeMM; + qDebug() << " " << "resolution = " << resolution; + qDebug() << " " << "stretch = " << stretch_x << stretch_y; + qDebug() << " " << "origin = " << origin_x << origin_y; + qDebug() << " " << "dpi = " << dpi_x << dpi_y; + qDebug() << ""; +} + +static void draw_text_item_win(const QPointF &pos, const QTextItemInt &ti, HDC hdc, + const QTransform &xform, const QPointF &topLeft) +{ + QPointF baseline_pos = xform.inverted().map(xform.map(pos) - topLeft); + + SetTextAlign(hdc, TA_BASELINE); + SetBkMode(hdc, TRANSPARENT); + + const bool has_kerning = ti.f && ti.f->kerning(); + + HFONT hfont = nullptr; + bool deleteFont = false; + + if (ti.fontEngine->type() == QFontEngine::Win) { + if (ti.fontEngine->supportsTransformation(QTransform::fromScale(0.5, 0.5))) // is TrueType font? + hfont = static_cast<HFONT>(ti.fontEngine->handle()); + } +#if QT_CONFIG(directwrite) + else if (ti.fontEngine->type() == QFontEngine::DirectWrite) { + QWindowsFontEngineDirectWrite *fedw = static_cast<QWindowsFontEngineDirectWrite *>(ti.fontEngine); + hfont = fedw->createHFONT(); + if (hfont) + deleteFont = true; + } +#endif + + if (!hfont) + hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); + + HGDIOBJ old_font = SelectObject(hdc, hfont); + unsigned int options = ETO_GLYPH_INDEX; + QGlyphLayout glyphs = ti.glyphs; + + bool fast = !has_kerning && !(ti.flags & QTextItem::RightToLeft); + for (int i = 0; fast && i < glyphs.numGlyphs; i++) { + if (glyphs.offsets[i].x != 0 || glyphs.offsets[i].y != 0 || glyphs.justifications[i].space_18d6 != 0 + || glyphs.attributes[i].dontPrint) { + fast = false; + break; + } + } + + // Scale, rotate and translate here. + XFORM win_xform; + win_xform.eM11 = xform.m11(); + win_xform.eM12 = xform.m12(); + win_xform.eM21 = xform.m21(); + win_xform.eM22 = xform.m22(); + win_xform.eDx = xform.dx(); + win_xform.eDy = xform.dy(); + + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &win_xform); + + if (fast) { + // fast path + QVarLengthArray<wchar_t> g(glyphs.numGlyphs); + for (int i = 0; i < glyphs.numGlyphs; ++i) + g[i] = glyphs.glyphs[i]; + ExtTextOut(hdc, + qRound(baseline_pos.x() + glyphs.offsets[0].x.toReal()), + qRound(baseline_pos.y() + glyphs.offsets[0].y.toReal()), + options, 0, g.constData(), glyphs.numGlyphs, 0); + } else { + QVarLengthArray<QFixedPoint> positions; + QVarLengthArray<glyph_t> _glyphs; + + QTransform matrix = QTransform::fromTranslate(baseline_pos.x(), baseline_pos.y()); + ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, + _glyphs, positions); + if (_glyphs.isEmpty()) { + SelectObject(hdc, old_font); + return; + } + + options |= ETO_PDY; + QVarLengthArray<INT> glyphDistances(_glyphs.size() * 2); + QVarLengthArray<wchar_t> g(_glyphs.size()); + const int lastGlyph = _glyphs.size() - 1; + for (int i = 0; i < lastGlyph; ++i) { + glyphDistances[i * 2] = qRound(positions[i + 1].x) - qRound(positions[i].x); + glyphDistances[i * 2 + 1] = qRound(positions[i + 1].y) - qRound(positions[i].y); + g[i] = _glyphs[i]; + } + glyphDistances[lastGlyph * 2] = 0; + glyphDistances[lastGlyph * 2 + 1] = 0; + g[lastGlyph] = _glyphs[lastGlyph]; + ExtTextOut(hdc, qRound(positions[0].x), qRound(positions[0].y), options, nullptr, + g.constData(), _glyphs.size(), + glyphDistances.data()); + } + + win_xform.eM11 = win_xform.eM22 = 1.0; + win_xform.eM12 = win_xform.eM21 = win_xform.eDx = win_xform.eDy = 0.0; + SetWorldTransform(hdc, &win_xform); + + SelectObject(hdc, old_font); + + if (deleteFont) + DeleteObject(hfont); +} + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER diff --git a/src/printsupport/platform/windows/qprintengine_win_p.h b/src/printsupport/platform/windows/qprintengine_win_p.h new file mode 100644 index 0000000000..995c31ff1e --- /dev/null +++ b/src/printsupport/platform/windows/qprintengine_win_p.h @@ -0,0 +1,198 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPRINTENGINE_WIN_P_H +#define QPRINTENGINE_WIN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtPrintSupport/private/qtprintsupportglobal_p.h> + +#ifndef QT_NO_PRINTER + +#include <QtGui/qpaintengine.h> +#include <QtGui/qpagelayout.h> +#include <QtPrintSupport/QPrintEngine> +#include <QtPrintSupport/QPrinter> +#include <private/qpaintengine_alpha_p.h> +#include <private/qprintdevice_p.h> +#include <QtCore/qt_windows.h> + +QT_BEGIN_NAMESPACE + +class QWin32PrintEnginePrivate; +class QPrinterPrivate; +class QPainterState; + +class Q_PRINTSUPPORT_EXPORT QWin32PrintEngine : public QAlphaPaintEngine, public QPrintEngine +{ + Q_DECLARE_PRIVATE(QWin32PrintEngine) +public: + QWin32PrintEngine(QPrinter::PrinterMode mode, const QString &deviceId); + + // override QWin32PaintEngine + bool begin(QPaintDevice *dev) override; + bool end() override; + + void updateState(const QPaintEngineState &state) override; + + void updateMatrix(const QTransform &matrix); + void updateClipPath(const QPainterPath &clip, Qt::ClipOperation op); + + void drawPath(const QPainterPath &path) override; + void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override; + void drawTextItem(const QPointF &p, const QTextItem &textItem) override; + + void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override; + void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &p) override; + void setProperty(PrintEnginePropertyKey key, const QVariant &value) override; + QVariant property(PrintEnginePropertyKey key) const override; + + bool newPage() override; + bool abort() override; + int metric(QPaintDevice::PaintDeviceMetric) const override; + + QPrinter::PrinterState printerState() const override; + + QPaintEngine::Type type() const override { return Windows; } + + HDC getDC() const; + void releaseDC(HDC) const; + + /* Used by print/page setup dialogs */ + void setGlobalDevMode(HGLOBAL globalDevNames, HGLOBAL globalDevMode); + HGLOBAL *createGlobalDevNames(); + HGLOBAL globalDevMode(); + +private: + friend class QPrintDialog; + friend class QPageSetupDialog; +}; + +class QWin32PrintEnginePrivate : public QAlphaPaintEnginePrivate +{ + Q_DECLARE_PUBLIC(QWin32PrintEngine) +public: + QWin32PrintEnginePrivate() : + printToFile(false), reinit(false), + complex_xform(false), has_pen(false), has_brush(false), has_custom_paper_size(false), + embed_fonts(true) + { + } + + ~QWin32PrintEnginePrivate(); + + + /* Initializes the printer data based on the current printer name. This + function creates a DEVMODE struct, HDC and a printer handle. If these + structures are already in use, they are freed using release + */ + void initialize(); + + /* Initializes data in the print engine whenever the HDC has been renewed + */ + void initHDC(); + + /* Releases all the handles the printer currently holds, HDC, DEVMODE, + etc and resets the corresponding members to 0. */ + void release(); + + /* Resets the DC with changes in devmode. If the printer is active + this function only sets the reinit variable to true so it + is handled in the next begin or newpage. */ + void doReinit(); + + static void initializeDevMode(DEVMODE *); + + bool resetDC(); + + void strokePath(const QPainterPath &path, const QColor &color); + void fillPath(const QPainterPath &path, const QColor &color); + + void composeGdiPath(const QPainterPath &path); + void fillPath_dev(const QPainterPath &path, const QColor &color); + void strokePath_dev(const QPainterPath &path, const QColor &color, qreal width); + + void setPageSize(const QPageSize &pageSize); + void updatePageLayout(); + void updateMetrics(); + void debugMetrics() const; + + // Windows GDI printer references. + HANDLE hPrinter = nullptr; + + HGLOBAL globalDevMode = nullptr; + DEVMODE *devMode = nullptr; + PRINTER_INFO_2 *pInfo = nullptr; + HGLOBAL hMem = nullptr; + + HDC hdc = nullptr; + + // True if devMode was allocated separately from pInfo. + bool ownsDevMode = false; + + QPrinter::PrinterMode mode = QPrinter::ScreenResolution; + + // Print Device + QPrintDevice m_printDevice; + + // Document info + QString docName; + QString m_creator; + QString fileName; + + QPrinter::PrinterState state = QPrinter::Idle; + int resolution = 0; + + // Page Layout + QPageLayout m_pageLayout{QPageSize(QPageSize::A4), + QPageLayout::Portrait, QMarginsF{0, 0, 0, 0}}; + // Page metrics cache + QRect m_paintRectPixels; + QSize m_paintSizeMM; + + // Windows painting + qreal stretch_x = 1; + qreal stretch_y = 1; + int origin_x = 0; + int origin_y = 0; + + int dpi_x = 96; + int dpi_y = 96; + int dpi_display = 96; + int num_copies = 1; + + uint printToFile : 1; + uint reinit : 1; + + uint complex_xform : 1; + uint has_pen : 1; + uint has_brush : 1; + uint has_custom_paper_size : 1; + uint embed_fonts : 1; + + uint txop = 0; // QTransform::TxNone + + QColor brush_color; + QPen pen; + QColor pen_color; + QSizeF paper_size; // In points + + QTransform painterMatrix; + QTransform matrix; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER + +#endif // QPRINTENGINE_WIN_P_H diff --git a/src/printsupport/platform/windows/qwindowsprintdevice.cpp b/src/printsupport/platform/windows/qwindowsprintdevice.cpp new file mode 100644 index 0000000000..9445871ed7 --- /dev/null +++ b/src/printsupport/platform/windows/qwindowsprintdevice.cpp @@ -0,0 +1,520 @@ +// Copyright (C) 2014 John Layt <jlayt@kde.org> +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsprintdevice_p.h" + +#include <QtCore/qdebug.h> + +#ifndef DC_COLLATE +# define DC_COLLATE 22 +#endif + +QT_BEGIN_NAMESPACE + +using WindowsPrinterLookup = QList<QWindowsPrinterInfo>; +Q_GLOBAL_STATIC(WindowsPrinterLookup, windowsDeviceLookup); + +extern qreal qt_pointMultiplier(QPageLayout::Unit unit); + +static inline uint qwcsnlen(const wchar_t *str, uint maxlen) +{ + uint length = 0; + if (str) { + while (length < maxlen && *str++) + length++; + } + return length; +} + +static LPDEVMODE getDevmode(HANDLE hPrinter, const QString &printerId) +{ + LPWSTR printerIdUtf16 = const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(printerId.utf16())); + // Allocate the required DEVMODE buffer + LONG dmSize = DocumentProperties(nullptr, hPrinter, printerIdUtf16, nullptr, nullptr, 0); + if (dmSize <= 0) + return nullptr; + LPDEVMODE pDevMode = reinterpret_cast<LPDEVMODE>(malloc(dmSize)); + // Get the default DevMode + LONG result = DocumentProperties(nullptr, hPrinter, printerIdUtf16, pDevMode, nullptr, DM_OUT_BUFFER); + if (result != IDOK) { + free(pDevMode); + pDevMode = nullptr; + } + return pDevMode; +} + +QWindowsPrintDevice::QWindowsPrintDevice() + : QPlatformPrintDevice(), + m_hPrinter(0) +{ +} + +QWindowsPrintDevice::QWindowsPrintDevice(const QString &id) + : QPlatformPrintDevice(id), + m_hPrinter(0) +{ + // First do a fast lookup to see if printer exists, if it does then open it + if (!id.isEmpty() && QWindowsPrintDevice::availablePrintDeviceIds().contains(id)) { + if (OpenPrinter(const_cast<LPWSTR>(wcharId()), &m_hPrinter, nullptr)) { + DWORD needed = 0; + GetPrinter(m_hPrinter, 2, 0, 0, &needed); + QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); + if (GetPrinter(m_hPrinter, 2, buffer.data(), needed, &needed)) { + PPRINTER_INFO_2 info = reinterpret_cast<PPRINTER_INFO_2>(buffer.data()); + m_name = QString::fromWCharArray(info->pPrinterName); + m_location = QString::fromWCharArray(info->pLocation); + m_makeAndModel = QString::fromWCharArray(info->pDriverName); // TODO Check is not available elsewhere + m_isRemote = info->Attributes & PRINTER_ATTRIBUTE_NETWORK; + } + QWindowsPrinterInfo m_info; + m_info.m_id = m_id; + m_info.m_name = m_name; + m_info.m_location = m_location; + m_info.m_makeAndModel = m_makeAndModel; + m_info.m_isRemote = m_isRemote; + m_infoIndex = windowsDeviceLookup()->indexOf(m_info); + if (m_infoIndex != -1) { + m_info = windowsDeviceLookup()->at(m_infoIndex); + m_havePageSizes = m_info.m_havePageSizes; + m_pageSizes = m_info.m_pageSizes; + m_haveResolutions = m_info.m_haveResolutions; + m_resolutions = m_info.m_resolutions; + m_haveCopies = m_info.m_haveCopies; + m_supportsMultipleCopies = m_info.m_supportsMultipleCopies; + m_supportsCollateCopies = m_info.m_supportsCollateCopies; + m_haveMinMaxPageSizes = m_info.m_haveMinMaxPageSizes; + m_minimumPhysicalPageSize = m_info.m_minimumPhysicalPageSize; + m_maximumPhysicalPageSize = m_info.m_maximumPhysicalPageSize; + m_supportsCustomPageSizes = m_info.m_supportsCustomPageSizes; + m_haveInputSlots = m_info.m_haveInputSlots; + m_inputSlots = m_info.m_inputSlots; + m_haveOutputBins = m_info.m_haveOutputBins; + m_outputBins = m_info.m_outputBins; + m_haveDuplexModes = m_info.m_haveDuplexModes; + m_duplexModes = m_info.m_duplexModes; + m_haveColorModes = m_info.m_haveColorModes; + m_colorModes = m_info.m_colorModes; + m_infoIndex = windowsDeviceLookup()->indexOf(m_info); + } else { + windowsDeviceLookup()->append(m_info); + m_infoIndex = windowsDeviceLookup()->count() - 1; + } + } + } +} + +QWindowsPrintDevice::~QWindowsPrintDevice() +{ + ClosePrinter(m_hPrinter); +} + +bool QWindowsPrintDevice::isValid() const +{ + return m_hPrinter; +} + +bool QWindowsPrintDevice::isDefault() const +{ + return m_id == defaultPrintDeviceId(); +} + +QPrint::DeviceState QWindowsPrintDevice::state() const +{ + DWORD needed = 0; + GetPrinter(m_hPrinter, 6, 0, 0, &needed); + QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); + + if (GetPrinter(m_hPrinter, 6, buffer.data(), needed, &needed)) { + PPRINTER_INFO_6 info = reinterpret_cast<PPRINTER_INFO_6>(buffer.data()); + // TODO Check mapping + if (info->dwStatus == 0 + || (info->dwStatus & PRINTER_STATUS_WAITING) == PRINTER_STATUS_WAITING + || (info->dwStatus & PRINTER_STATUS_POWER_SAVE) == PRINTER_STATUS_POWER_SAVE) { + return QPrint::Idle; + } else if ((info->dwStatus & PRINTER_STATUS_PRINTING) == PRINTER_STATUS_PRINTING + || (info->dwStatus & PRINTER_STATUS_BUSY) == PRINTER_STATUS_BUSY + || (info->dwStatus & PRINTER_STATUS_INITIALIZING) == PRINTER_STATUS_INITIALIZING + || (info->dwStatus & PRINTER_STATUS_IO_ACTIVE) == PRINTER_STATUS_IO_ACTIVE + || (info->dwStatus & PRINTER_STATUS_PROCESSING) == PRINTER_STATUS_PROCESSING + || (info->dwStatus & PRINTER_STATUS_WARMING_UP) == PRINTER_STATUS_WARMING_UP) { + return QPrint::Active; + } + } + + return QPrint::Error; +} + +void QWindowsPrintDevice::loadPageSizes() const +{ + // Get the number of paper sizes and check all 3 attributes have same count + const int paperCount = DeviceCapabilities(wcharId(), nullptr, DC_PAPERNAMES, nullptr, nullptr); + if (paperCount > 0 + && DeviceCapabilities(wcharId(), nullptr, DC_PAPERSIZE, nullptr, nullptr) == paperCount + && DeviceCapabilities(wcharId(), nullptr, DC_PAPERS, nullptr, nullptr) == paperCount) { + + QScopedArrayPointer<wchar_t> paperNames(new wchar_t[paperCount*64]); + QScopedArrayPointer<POINT> winSizes(new POINT[paperCount]); + QScopedArrayPointer<wchar_t> papers(new wchar_t[paperCount]); + + // Get the details and match the default paper size + if (DeviceCapabilities(wcharId(), nullptr, DC_PAPERNAMES, paperNames.data(), nullptr) == paperCount + && DeviceCapabilities(wcharId(), nullptr, DC_PAPERSIZE, + reinterpret_cast<wchar_t *>(winSizes.data()), nullptr) == paperCount + && DeviceCapabilities(wcharId(), nullptr, DC_PAPERS, papers.data(), nullptr) == paperCount) { + + // Returned size is in tenths of a millimeter + const qreal multiplier = qt_pointMultiplier(QPageLayout::Millimeter); + for (int i = 0; i < int(paperCount); ++i) { + QSize size = QSize(qRound((winSizes[i].x / 10.0) * multiplier), qRound((winSizes[i].y / 10.0) * multiplier)); + wchar_t *paper = paperNames.data() + (i * 64); + QString name = QString::fromWCharArray(paper, qwcsnlen(paper, 64)); + m_pageSizes.append(createPageSize(papers[i], size, name)); + } + + } + } + + m_havePageSizes = true; + QWindowsPrinterInfo *info = windowsDeviceLookup()->data(); + info[m_infoIndex].m_havePageSizes = true; + info[m_infoIndex].m_pageSizes = m_pageSizes; +} + +QPageSize QWindowsPrintDevice::defaultPageSize() const +{ + if (!m_havePageSizes) + loadPageSizes(); + + QPageSize pageSize; + + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default paper size + if (pDevMode->dmFields & DM_PAPERSIZE) { + // Find the supported page size that matches, in theory default should be one of them + for (const QPageSize &ps : m_pageSizes) { + if (ps.windowsId() == pDevMode->dmPaperSize) { + pageSize = ps; + break; + } + } + } + // Clean-up + free(pDevMode); + } + + return pageSize; +} + +QMarginsF QWindowsPrintDevice::printableMargins(const QPageSize &pageSize, + QPageLayout::Orientation orientation, + int resolution) const +{ + // TODO This is slow, need to cache values or find better way! + // Modify the DevMode to get the DC printable margins in device pixels + QMarginsF margins = QMarginsF(0, 0, 0, 0); + DWORD needed = 0; + GetPrinter(m_hPrinter, 2, 0, 0, &needed); + QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); + if (GetPrinter(m_hPrinter, 2, buffer.data(), needed, &needed)) { + PPRINTER_INFO_2 info = reinterpret_cast<PPRINTER_INFO_2>(buffer.data()); + LPDEVMODE devMode = info->pDevMode; + bool separateDevMode = false; + if (!devMode) { + // GetPrinter() didn't include the DEVMODE. Get it a different way. + devMode = getDevmode(m_hPrinter, m_id); + if (!devMode) + return margins; + separateDevMode = true; + } + + HDC pDC = CreateDC(nullptr, wcharId(), nullptr, devMode); + if (pageSize.id() == QPageSize::Custom || pageSize.windowsId() <= 0 || pageSize.windowsId() > DMPAPER_LAST) { + devMode->dmPaperSize = 0; + devMode->dmPaperWidth = pageSize.size(QPageSize::Millimeter).width() * 10.0; + devMode->dmPaperLength = pageSize.size(QPageSize::Millimeter).height() * 10.0; + } else { + devMode->dmPaperSize = pageSize.windowsId(); + } + devMode->dmPrintQuality = resolution; + devMode->dmOrientation = orientation == QPageLayout::Portrait ? DMORIENT_PORTRAIT : DMORIENT_LANDSCAPE; + ResetDC(pDC, devMode); + const int dpiWidth = GetDeviceCaps(pDC, LOGPIXELSX); + const int dpiHeight = GetDeviceCaps(pDC, LOGPIXELSY); + const qreal wMult = 72.0 / dpiWidth; + const qreal hMult = 72.0 / dpiHeight; + const qreal physicalWidth = GetDeviceCaps(pDC, PHYSICALWIDTH) * wMult; + const qreal physicalHeight = GetDeviceCaps(pDC, PHYSICALHEIGHT) * hMult; + const qreal printableWidth = GetDeviceCaps(pDC, HORZRES) * wMult; + const qreal printableHeight = GetDeviceCaps(pDC, VERTRES) * hMult; + const qreal leftMargin = GetDeviceCaps(pDC, PHYSICALOFFSETX)* wMult; + const qreal topMargin = GetDeviceCaps(pDC, PHYSICALOFFSETY) * hMult; + const qreal rightMargin = physicalWidth - leftMargin - printableWidth; + const qreal bottomMargin = physicalHeight - topMargin - printableHeight; + margins = QMarginsF(leftMargin, topMargin, rightMargin, bottomMargin); + if (separateDevMode) + free(devMode); + DeleteDC(pDC); + } + return margins; +} + +void QWindowsPrintDevice::loadResolutions() const +{ + const int resCount = DeviceCapabilities(wcharId(), nullptr, DC_ENUMRESOLUTIONS, nullptr, nullptr); + if (resCount > 0) { + QScopedArrayPointer<LONG> resolutions(new LONG[resCount*2]); + // Get the details and match the default paper size + if (DeviceCapabilities(wcharId(), nullptr, DC_ENUMRESOLUTIONS, + reinterpret_cast<LPWSTR>(resolutions.data()), nullptr) == resCount) { + for (int i = 0; i < int(resCount * 2); i += 2) + m_resolutions.append(resolutions[i+1]); + } + } + m_haveResolutions = true; + QWindowsPrinterInfo *info = windowsDeviceLookup()->data(); + info[m_infoIndex].m_haveResolutions = true; + info[m_infoIndex].m_resolutions = m_resolutions; +} + +int QWindowsPrintDevice::defaultResolution() const +{ + int resolution = 72; // TODO Set a sensible default? + + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default resolution + if (pDevMode->dmFields & DM_YRESOLUTION) { + if (pDevMode->dmPrintQuality > 0) + resolution = pDevMode->dmPrintQuality; + else + resolution = pDevMode->dmYResolution; + } + // Clean-up + free(pDevMode); + } + return resolution; +} + +void QWindowsPrintDevice::loadInputSlots() const +{ + const auto printerId = wcharId(); + const int binCount = DeviceCapabilities(printerId, nullptr, DC_BINS, nullptr, nullptr); + if (binCount > 0 + && DeviceCapabilities(printerId, nullptr, DC_BINNAMES, nullptr, nullptr) == binCount) { + + QScopedArrayPointer<WORD> bins(new WORD[binCount]); + QScopedArrayPointer<wchar_t> binNames(new wchar_t[binCount*24]); + + // Get the details and match the default paper size + if (DeviceCapabilities(printerId, nullptr, DC_BINS, + reinterpret_cast<LPWSTR>(bins.data()), nullptr) == binCount + && DeviceCapabilities(printerId, nullptr, DC_BINNAMES, binNames.data(), + nullptr) == binCount) { + + for (int i = 0; i < int(binCount); ++i) { + wchar_t *binName = binNames.data() + (i * 24); + QString name = QString::fromWCharArray(binName, qwcsnlen(binName, 24)); + m_inputSlots.append(QPrintUtils::paperBinToInputSlot(bins[i], name)); + } + + } + } + + m_haveInputSlots = true; + QWindowsPrinterInfo *info = windowsDeviceLookup()->data(); + info[m_infoIndex].m_haveInputSlots = true; + info[m_infoIndex].m_inputSlots = m_inputSlots; +} + +QPrint::InputSlot QWindowsPrintDevice::defaultInputSlot() const +{ + QPrint::InputSlot inputSlot = QPlatformPrintDevice::defaultInputSlot(); + + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default input slot + if (pDevMode->dmFields & DM_DEFAULTSOURCE) { + QPrint::InputSlot tempSlot = + QPrintUtils::paperBinToInputSlot(pDevMode->dmDefaultSource, QString()); + const auto inputSlots = supportedInputSlots(); + for (const QPrint::InputSlot &slot : inputSlots) { + if (slot.key == tempSlot.key) { + inputSlot = slot; + break; + } + } + } + // Clean-up + free(pDevMode); + } + return inputSlot; +} + +void QWindowsPrintDevice::loadOutputBins() const +{ + m_outputBins.append(QPlatformPrintDevice::defaultOutputBin()); + m_haveOutputBins = true; + QWindowsPrinterInfo *info = windowsDeviceLookup()->data(); + info[m_infoIndex].m_haveOutputBins = true; + info[m_infoIndex].m_outputBins = m_outputBins; +} + +void QWindowsPrintDevice::loadDuplexModes() const +{ + m_duplexModes.append(QPrint::DuplexNone); + DWORD duplex = DeviceCapabilities(wcharId(), nullptr, DC_DUPLEX, nullptr, nullptr); + if (int(duplex) == 1) { + // TODO Assume if duplex flag supports both modes + m_duplexModes.append(QPrint::DuplexAuto); + m_duplexModes.append(QPrint::DuplexLongSide); + m_duplexModes.append(QPrint::DuplexShortSide); + } + m_haveDuplexModes = true; + QWindowsPrinterInfo *info = windowsDeviceLookup()->data(); + info[m_infoIndex].m_haveDuplexModes = true; + info[m_infoIndex].m_duplexModes = m_duplexModes; +} + +QPrint::DuplexMode QWindowsPrintDevice::defaultDuplexMode() const +{ + QPrint::DuplexMode duplexMode = QPrint::DuplexNone; + + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default duplex mode + if (pDevMode->dmFields & DM_DUPLEX) { + if (pDevMode->dmDuplex == DMDUP_VERTICAL) + duplexMode = QPrint::DuplexLongSide; + else if (pDevMode->dmDuplex == DMDUP_HORIZONTAL) + duplexMode = QPrint::DuplexShortSide; + } + // Clean-up + free(pDevMode); + } + return duplexMode; +} + +void QWindowsPrintDevice::loadColorModes() const +{ + m_colorModes.append(QPrint::GrayScale); + DWORD color = DeviceCapabilities(wcharId(), nullptr, DC_COLORDEVICE, nullptr, nullptr); + if (int(color) == 1) + m_colorModes.append(QPrint::Color); + m_haveColorModes = true; + QWindowsPrinterInfo *info = windowsDeviceLookup()->data(); + info[m_infoIndex].m_haveColorModes = true; + info[m_infoIndex].m_colorModes = m_colorModes; +} + +QPrint::ColorMode QWindowsPrintDevice::defaultColorMode() const +{ + if (!m_haveColorModes) + loadColorModes(); + if (!m_colorModes.contains(QPrint::Color)) + return QPrint::GrayScale; + + QPrint::ColorMode colorMode = QPrint::GrayScale; + + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default color mode + if (pDevMode->dmFields & DM_COLOR && pDevMode->dmColor == DMCOLOR_COLOR) + colorMode = QPrint::Color; + // Clean-up + free(pDevMode); + } + return colorMode; +} + +QStringList QWindowsPrintDevice::availablePrintDeviceIds() +{ + QStringList list; + DWORD needed = 0; + DWORD returned = 0; + if ((!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 4, 0, 0, &needed, &returned) + && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + || !needed) { + return list; + } + QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); + if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 4, buffer.data(), needed, &needed, &returned)) + return list; + PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer.data()); + for (uint i = 0; i < returned; ++i) + list.append(QString::fromWCharArray(infoList[i].pPrinterName)); + return list; +} + +QString QWindowsPrintDevice::defaultPrintDeviceId() +{ + DWORD size = 0; + if (GetDefaultPrinter(nullptr, &size) == ERROR_FILE_NOT_FOUND || size < 2) + return QString(); + + QScopedArrayPointer<wchar_t> name(new wchar_t[size]); + GetDefaultPrinter(name.data(), &size); + return QString::fromWCharArray(name.data()); +} + +void QWindowsPrintDevice::loadCopiesSupport() const +{ + auto printerId = wcharId(); + m_supportsMultipleCopies = (DeviceCapabilities(printerId, nullptr, DC_COPIES, nullptr, nullptr) > 1); + m_supportsCollateCopies = DeviceCapabilities(printerId, nullptr, DC_COLLATE, nullptr, nullptr); + m_haveCopies = true; + QWindowsPrinterInfo *info = windowsDeviceLookup()->data(); + info[m_infoIndex].m_haveCopies = true; + info[m_infoIndex].m_supportsMultipleCopies = m_supportsMultipleCopies; + info[m_infoIndex].m_supportsCollateCopies = m_supportsCollateCopies; +} + +bool QWindowsPrintDevice::supportsCollateCopies() const +{ + if (!m_haveCopies) + loadCopiesSupport(); + return m_supportsCollateCopies; +} + +bool QWindowsPrintDevice::supportsMultipleCopies() const +{ + if (!m_haveCopies) + loadCopiesSupport(); + return m_supportsMultipleCopies; +} + +bool QWindowsPrintDevice::supportsCustomPageSizes() const +{ + if (!m_haveMinMaxPageSizes) + loadMinMaxPageSizes(); + return m_supportsCustomPageSizes; +} + +QSize QWindowsPrintDevice::minimumPhysicalPageSize() const +{ + if (!m_haveMinMaxPageSizes) + loadMinMaxPageSizes(); + return m_minimumPhysicalPageSize; +} + +QSize QWindowsPrintDevice::maximumPhysicalPageSize() const +{ + if (!m_haveMinMaxPageSizes) + loadMinMaxPageSizes(); + return m_maximumPhysicalPageSize; +} + +void QWindowsPrintDevice::loadMinMaxPageSizes() const +{ + // Min/Max custom size is in tenths of a millimeter + const qreal multiplier = qt_pointMultiplier(QPageLayout::Millimeter); + auto printerId = wcharId(); + DWORD min = DeviceCapabilities(printerId, nullptr, DC_MINEXTENT, nullptr, nullptr); + m_minimumPhysicalPageSize = QSize((LOWORD(min) / 10.0) * multiplier, (HIWORD(min) / 10.0) * multiplier); + DWORD max = DeviceCapabilities(printerId, nullptr, DC_MAXEXTENT, nullptr, nullptr); + m_maximumPhysicalPageSize = QSize((LOWORD(max) / 10.0) * multiplier, (HIWORD(max) / 10.0) * multiplier); + m_supportsCustomPageSizes = (m_maximumPhysicalPageSize.width() > 0 && m_maximumPhysicalPageSize.height() > 0); + m_haveMinMaxPageSizes = true; + QWindowsPrinterInfo *info = windowsDeviceLookup()->data(); + info[m_infoIndex].m_haveCopies = true; + info[m_infoIndex].m_supportsMultipleCopies = m_supportsMultipleCopies; + info[m_infoIndex].m_supportsCollateCopies = m_supportsCollateCopies; +} + +QT_END_NAMESPACE diff --git a/src/printsupport/platform/windows/qwindowsprintdevice_p.h b/src/printsupport/platform/windows/qwindowsprintdevice_p.h new file mode 100644 index 0000000000..edca1894e6 --- /dev/null +++ b/src/printsupport/platform/windows/qwindowsprintdevice_p.h @@ -0,0 +1,119 @@ +// Copyright (C) 2014 John Layt <jlayt@kde.org> +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSPRINTDEVICE_H +#define QWINDOWSPRINTDEVICE_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> + +#include <QtPrintSupport/qtprintsupportglobal.h> +#include <QtCore/qt_windows.h> +#include <QtCore/private/qglobal_p.h> + +QT_BEGIN_NAMESPACE + +class Q_PRINTSUPPORT_EXPORT QWindowsPrinterInfo +{ +public: + bool operator==(const QWindowsPrinterInfo &other) const + { + // We only need to check if these are the same for matching up + return m_id == other.m_id && m_name == other.m_name && + m_location == other.m_location && + m_makeAndModel == other.m_makeAndModel && + m_isRemote == other.m_isRemote; + } + QString m_id; + QString m_name; + QString m_location; + QString m_makeAndModel; + QList<QPageSize> m_pageSizes; + QList<int> m_resolutions; + QList<QPrint::InputSlot> m_inputSlots; + QList<QPrint::OutputBin> m_outputBins; + QList<QPrint::DuplexMode> m_duplexModes; + QList<QPrint::ColorMode> m_colorModes; + QSize m_minimumPhysicalPageSize; + QSize m_maximumPhysicalPageSize; + bool m_isRemote = false; + bool m_havePageSizes = false; + bool m_haveResolutions = false; + bool m_haveCopies = false; + bool m_supportsMultipleCopies = false; + bool m_supportsCollateCopies = false; + bool m_haveMinMaxPageSizes = false; + bool m_supportsCustomPageSizes = false; + bool m_haveInputSlots = false; + bool m_haveOutputBins = false; + bool m_haveDuplexModes = false; + bool m_haveColorModes = false; +}; + +class Q_PRINTSUPPORT_EXPORT QWindowsPrintDevice : public QPlatformPrintDevice +{ +public: + QWindowsPrintDevice(); + explicit QWindowsPrintDevice(const QString &id); + virtual ~QWindowsPrintDevice(); + + 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::DuplexMode defaultDuplexMode() const override; + + QPrint::ColorMode defaultColorMode() const override; + + static QStringList availablePrintDeviceIds(); + static QString defaultPrintDeviceId(); + + bool supportsCollateCopies() const override; + bool supportsMultipleCopies() const override; + bool supportsCustomPageSizes() const override; + QSize minimumPhysicalPageSize() const override; + QSize maximumPhysicalPageSize() const override; + +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; + void loadCopiesSupport() const; + void loadMinMaxPageSizes() const; + +private: + LPCWSTR wcharId() const { return reinterpret_cast<LPCWSTR>(m_id.utf16()); } + + HANDLE m_hPrinter; + mutable bool m_haveCopies; + mutable bool m_haveMinMaxPageSizes; + int m_infoIndex; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSPRINTDEVICE_H diff --git a/src/printsupport/platform/windows/qwindowsprinterinfo.cpp b/src/printsupport/platform/windows/qwindowsprinterinfo.cpp new file mode 100644 index 0000000000..73040de60f --- /dev/null +++ b/src/printsupport/platform/windows/qwindowsprinterinfo.cpp @@ -0,0 +1,86 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qprinterinfo.h" +#include "qprinterinfo_p.h" + +#include <qstringlist.h> + +#include <qt_windows.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +#ifndef QT_NO_PRINTER + +extern QPrinter::PaperSize mapDevmodePaperSize(int s); + +//QList<QPrinterInfo> QPrinterInfo::availablePrinters() +//{ +// QList<QPrinterInfo> printers; + +// DWORD needed = 0; +// DWORD returned = 0; +// if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned)) { +// LPBYTE buffer = new BYTE[needed]; +// if (EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, buffer, needed, &needed, &returned)) { +// PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer); +// QPrinterInfo defPrn = defaultPrinter(); +// for (uint i = 0; i < returned; ++i) { +// QString printerName(QString::fromWCharArray(infoList[i].pPrinterName)); + +// QPrinterInfo printerInfo(printerName); +// if (printerInfo.printerName() == defPrn.printerName()) +// printerInfo.d_ptr->isDefault = true; +// printers.append(printerInfo); +// } +// } +// delete [] buffer; +// } + +// return printers; +//} + +//QPrinterInfo QPrinterInfo::defaultPrinter() +//{ +// QString noPrinters("qt_no_printers"_L1); +// wchar_t buffer[256]; +// GetProfileString(L"windows", L"device", (wchar_t*)noPrinters.utf16(), buffer, 256); +// QString output = QString::fromWCharArray(buffer); +// if (output != noPrinters) { +// // Filter out the name of the printer, which should be everything before a comma. +// QString printerName = output.split(u',').value(0); +// QPrinterInfo printerInfo(printerName); +// printerInfo.d_ptr->isDefault = true; +// return printerInfo; +// } + +// return QPrinterInfo(); +//} + +//QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const +//{ +// const Q_D(QPrinterInfo); + +// QList<QPrinter::PaperSize> paperSizes; +// if (isNull()) +// return paperSizes; + +// DWORD size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()), +// NULL, DC_PAPERS, NULL, NULL); +// if ((int)size != -1) { +// wchar_t *papers = new wchar_t[size]; +// size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()), +// NULL, DC_PAPERS, papers, NULL); +// for (int c = 0; c < (int)size; ++c) +// paperSizes.append(mapDevmodePaperSize(papers[c])); +// delete [] papers; +// } + +// return paperSizes; +//} + +#endif // QT_NO_PRINTER + +QT_END_NAMESPACE diff --git a/src/printsupport/platform/windows/qwindowsprintersupport.cpp b/src/printsupport/platform/windows/qwindowsprintersupport.cpp new file mode 100644 index 0000000000..7cbd4dc491 --- /dev/null +++ b/src/printsupport/platform/windows/qwindowsprintersupport.cpp @@ -0,0 +1,76 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsprintersupport_p.h" + +#ifndef QT_NO_PRINTER + +#include "qwindowsprintdevice_p.h" + +#include <QtCore/QStringList> +#include <private/qprintengine_win_p.h> +#include <private/qprintdevice_p.h> + +#define QT_STATICPLUGIN +#include <qpa/qplatformprintplugin.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +QWindowsPrinterSupport::QWindowsPrinterSupport() + : QPlatformPrinterSupport() +{ +} + +QWindowsPrinterSupport::~QWindowsPrinterSupport() +{ +} + +QPrintEngine *QWindowsPrinterSupport::createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId) +{ + return new QWin32PrintEngine(printerMode, deviceId); +} + +QPaintEngine *QWindowsPrinterSupport::createPaintEngine(QPrintEngine *engine, QPrinter::PrinterMode printerMode) +{ + Q_UNUSED(printerMode); + return static_cast<QWin32PrintEngine *>(engine); +} + +QPrintDevice QWindowsPrinterSupport::createPrintDevice(const QString &id) +{ + return QPlatformPrinterSupport::createPrintDevice(new QWindowsPrintDevice(id)); +} + +QStringList QWindowsPrinterSupport::availablePrintDeviceIds() const +{ + return QWindowsPrintDevice::availablePrintDeviceIds(); +} + +QString QWindowsPrinterSupport::defaultPrintDeviceId() const +{ + return QWindowsPrintDevice::defaultPrintDeviceId(); +} + +class QWindowsPrinterSupportPlugin : public QPlatformPrinterSupportPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QPlatformPrinterSupportFactoryInterface_iid FILE "windows.json") + +public: + QPlatformPrinterSupport *create(const QString &) override; +}; + +QPlatformPrinterSupport *QWindowsPrinterSupportPlugin::create(const QString &key) +{ + if (key.compare(key, "windowsprintsupport"_L1, Qt::CaseInsensitive) == 0) + return new QWindowsPrinterSupport; + return nullptr; +} + +QT_END_NAMESPACE + +#include "qwindowsprintersupport.moc" + +#endif // QT_NO_PRINTER diff --git a/src/printsupport/platform/windows/qwindowsprintersupport_p.h b/src/printsupport/platform/windows/qwindowsprintersupport_p.h new file mode 100644 index 0000000000..cee04e97d6 --- /dev/null +++ b/src/printsupport/platform/windows/qwindowsprintersupport_p.h @@ -0,0 +1,44 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef WINDOWSPRINTERSUPPORT_H +#define WINDOWSPRINTERSUPPORT_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 <QtPrintSupport/qtprintsupportglobal.h> + +#include <qpa/qplatformprintersupport.h> +#include <private/qglobal_p.h> +#ifndef QT_NO_PRINTER + +QT_BEGIN_NAMESPACE + +class Q_PRINTSUPPORT_EXPORT QWindowsPrinterSupport : public QPlatformPrinterSupport +{ + Q_DISABLE_COPY_MOVE(QWindowsPrinterSupport) +public: + QWindowsPrinterSupport(); + ~QWindowsPrinterSupport() override; + + QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) override; + QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode) override; + + QPrintDevice createPrintDevice(const QString &id) override; + QStringList availablePrintDeviceIds() const override; + QString defaultPrintDeviceId() const override; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER +#endif // WINDOWSPRINTERSUPPORT_H diff --git a/src/printsupport/platform/windows/windows.json b/src/printsupport/platform/windows/windows.json new file mode 100644 index 0000000000..803052854e --- /dev/null +++ b/src/printsupport/platform/windows/windows.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "windowsprintsupport" ] +} |