Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwindowsprintdevice.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 John Layt <jlayt@kde.org>
2// Copyright (C) 2018 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6
7#include <QtCore/qdebug.h>
8
9#ifndef DC_COLLATE
10# define DC_COLLATE 22
11#endif
12
14
15using WindowsPrinterLookup = QList<QWindowsPrinterInfo>;
17
19
20static inline uint qwcsnlen(const wchar_t *str, uint maxlen)
21{
22 uint length = 0;
23 if (str) {
24 while (length < maxlen && *str++)
25 length++;
26 }
27 return length;
28}
29
30static LPDEVMODE getDevmode(HANDLE hPrinter, const QString &printerId)
31{
32 LPWSTR printerIdUtf16 = const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(printerId.utf16()));
33 // Allocate the required DEVMODE buffer
34 LONG dmSize = DocumentProperties(nullptr, hPrinter, printerIdUtf16, nullptr, nullptr, 0);
35 if (dmSize <= 0)
36 return nullptr;
37 LPDEVMODE pDevMode = reinterpret_cast<LPDEVMODE>(malloc(dmSize));
38 // Get the default DevMode
39 LONG result = DocumentProperties(nullptr, hPrinter, printerIdUtf16, pDevMode, nullptr, DM_OUT_BUFFER);
40 if (result != IDOK) {
41 free(pDevMode);
42 pDevMode = nullptr;
43 }
44 return pDevMode;
45}
46
52
55 m_hPrinter(0)
56{
57 // First do a fast lookup to see if printer exists, if it does then open it
58 if (!id.isEmpty() && QWindowsPrintDevice::availablePrintDeviceIds().contains(id)) {
59 if (OpenPrinter(const_cast<LPWSTR>(wcharId()), &m_hPrinter, nullptr)) {
60 DWORD needed = 0;
61 GetPrinter(m_hPrinter, 2, 0, 0, &needed);
62 QScopedArrayPointer<BYTE> buffer(new BYTE[needed]);
63 if (GetPrinter(m_hPrinter, 2, buffer.data(), needed, &needed)) {
64 PPRINTER_INFO_2 info = reinterpret_cast<PPRINTER_INFO_2>(buffer.data());
65 m_name = QString::fromWCharArray(info->pPrinterName);
67 m_makeAndModel = QString::fromWCharArray(info->pDriverName); // TODO Check is not available elsewhere
68 m_isRemote = info->Attributes & PRINTER_ATTRIBUTE_NETWORK;
69 }
71 m_info.m_id = m_id;
72 m_info.m_name = m_name;
73 m_info.m_location = m_location;
75 m_info.m_isRemote = m_isRemote;
76 m_infoIndex = windowsDeviceLookup()->indexOf(m_info);
77 if (m_infoIndex != -1) {
78 m_info = windowsDeviceLookup()->at(m_infoIndex);
80 m_pageSizes = m_info.m_pageSizes;
83 m_haveCopies = m_info.m_haveCopies;
86 m_haveMinMaxPageSizes = m_info.m_haveMinMaxPageSizes;
98 m_infoIndex = windowsDeviceLookup()->indexOf(m_info);
99 } else {
100 windowsDeviceLookup()->append(m_info);
101 m_infoIndex = windowsDeviceLookup()->count() - 1;
102 }
103 }
104 }
105}
106
108{
109 ClosePrinter(m_hPrinter);
110}
111
113{
114 return m_hPrinter;
115}
116
118{
119 return m_id == defaultPrintDeviceId();
120}
121
123{
124 DWORD needed = 0;
125 GetPrinter(m_hPrinter, 6, 0, 0, &needed);
126 QScopedArrayPointer<BYTE> buffer(new BYTE[needed]);
127
128 if (GetPrinter(m_hPrinter, 6, buffer.data(), needed, &needed)) {
129 PPRINTER_INFO_6 info = reinterpret_cast<PPRINTER_INFO_6>(buffer.data());
130 // TODO Check mapping
131 if (info->dwStatus == 0
132 || (info->dwStatus & PRINTER_STATUS_WAITING) == PRINTER_STATUS_WAITING
133 || (info->dwStatus & PRINTER_STATUS_POWER_SAVE) == PRINTER_STATUS_POWER_SAVE) {
134 return QPrint::Idle;
135 } else if ((info->dwStatus & PRINTER_STATUS_PRINTING) == PRINTER_STATUS_PRINTING
136 || (info->dwStatus & PRINTER_STATUS_BUSY) == PRINTER_STATUS_BUSY
137 || (info->dwStatus & PRINTER_STATUS_INITIALIZING) == PRINTER_STATUS_INITIALIZING
138 || (info->dwStatus & PRINTER_STATUS_IO_ACTIVE) == PRINTER_STATUS_IO_ACTIVE
139 || (info->dwStatus & PRINTER_STATUS_PROCESSING) == PRINTER_STATUS_PROCESSING
140 || (info->dwStatus & PRINTER_STATUS_WARMING_UP) == PRINTER_STATUS_WARMING_UP) {
141 return QPrint::Active;
142 }
143 }
144
145 return QPrint::Error;
146}
147
149{
150 // Get the number of paper sizes and check all 3 attributes have same count
151 const int paperCount = DeviceCapabilities(wcharId(), nullptr, DC_PAPERNAMES, nullptr, nullptr);
152 if (paperCount > 0
153 && DeviceCapabilities(wcharId(), nullptr, DC_PAPERSIZE, nullptr, nullptr) == paperCount
154 && DeviceCapabilities(wcharId(), nullptr, DC_PAPERS, nullptr, nullptr) == paperCount) {
155
156 QScopedArrayPointer<wchar_t> paperNames(new wchar_t[paperCount*64]);
157 QScopedArrayPointer<POINT> winSizes(new POINT[paperCount]);
158 QScopedArrayPointer<wchar_t> papers(new wchar_t[paperCount]);
159
160 // Get the details and match the default paper size
161 if (DeviceCapabilities(wcharId(), nullptr, DC_PAPERNAMES, paperNames.data(), nullptr) == paperCount
162 && DeviceCapabilities(wcharId(), nullptr, DC_PAPERSIZE,
163 reinterpret_cast<wchar_t *>(winSizes.data()), nullptr) == paperCount
164 && DeviceCapabilities(wcharId(), nullptr, DC_PAPERS, papers.data(), nullptr) == paperCount) {
165
166 // Returned size is in tenths of a millimeter
168 for (int i = 0; i < int(paperCount); ++i) {
169 QSize size = QSize(qRound((winSizes[i].x / 10.0) * multiplier), qRound((winSizes[i].y / 10.0) * multiplier));
170 wchar_t *paper = paperNames.data() + (i * 64);
171 QString name = QString::fromWCharArray(paper, qwcsnlen(paper, 64));
173 }
174
175 }
176 }
177
178 m_havePageSizes = true;
179 QWindowsPrinterInfo *info = windowsDeviceLookup()->data();
180 info[m_infoIndex].m_havePageSizes = true;
181 info[m_infoIndex].m_pageSizes = m_pageSizes;
182}
183
185{
186 if (!m_havePageSizes)
188
189 QPageSize pageSize;
190
191 if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) {
192 // Get the default paper size
193 if (pDevMode->dmFields & DM_PAPERSIZE) {
194 // Find the supported page size that matches, in theory default should be one of them
195 for (const QPageSize &ps : m_pageSizes) {
196 if (ps.windowsId() == pDevMode->dmPaperSize) {
197 pageSize = ps;
198 break;
199 }
200 }
201 }
202 // Clean-up
203 free(pDevMode);
204 }
205
206 return pageSize;
207}
208
210 QPageLayout::Orientation orientation,
211 int resolution) const
212{
213 // TODO This is slow, need to cache values or find better way!
214 // Modify the DevMode to get the DC printable margins in device pixels
215 QMarginsF margins = QMarginsF(0, 0, 0, 0);
216 DWORD needed = 0;
217 GetPrinter(m_hPrinter, 2, 0, 0, &needed);
218 QScopedArrayPointer<BYTE> buffer(new BYTE[needed]);
219 if (GetPrinter(m_hPrinter, 2, buffer.data(), needed, &needed)) {
220 PPRINTER_INFO_2 info = reinterpret_cast<PPRINTER_INFO_2>(buffer.data());
221 LPDEVMODE devMode = info->pDevMode;
222 bool separateDevMode = false;
223 if (!devMode) {
224 // GetPrinter() didn't include the DEVMODE. Get it a different way.
225 devMode = getDevmode(m_hPrinter, m_id);
226 if (!devMode)
227 return margins;
228 separateDevMode = true;
229 }
230
231 HDC pDC = CreateDC(nullptr, wcharId(), nullptr, devMode);
232 if (pageSize.id() == QPageSize::Custom || pageSize.windowsId() <= 0 || pageSize.windowsId() > DMPAPER_LAST) {
233 devMode->dmPaperSize = 0;
234 devMode->dmPaperWidth = pageSize.size(QPageSize::Millimeter).width() * 10.0;
235 devMode->dmPaperLength = pageSize.size(QPageSize::Millimeter).height() * 10.0;
236 } else {
237 devMode->dmPaperSize = pageSize.windowsId();
238 }
239 devMode->dmPrintQuality = resolution;
240 devMode->dmOrientation = orientation == QPageLayout::Portrait ? DMORIENT_PORTRAIT : DMORIENT_LANDSCAPE;
241 ResetDC(pDC, devMode);
242 const int dpiWidth = GetDeviceCaps(pDC, LOGPIXELSX);
243 const int dpiHeight = GetDeviceCaps(pDC, LOGPIXELSY);
244 const qreal wMult = 72.0 / dpiWidth;
245 const qreal hMult = 72.0 / dpiHeight;
246 const qreal physicalWidth = GetDeviceCaps(pDC, PHYSICALWIDTH) * wMult;
247 const qreal physicalHeight = GetDeviceCaps(pDC, PHYSICALHEIGHT) * hMult;
248 const qreal printableWidth = GetDeviceCaps(pDC, HORZRES) * wMult;
249 const qreal printableHeight = GetDeviceCaps(pDC, VERTRES) * hMult;
250 const qreal leftMargin = GetDeviceCaps(pDC, PHYSICALOFFSETX)* wMult;
251 const qreal topMargin = GetDeviceCaps(pDC, PHYSICALOFFSETY) * hMult;
252 const qreal rightMargin = physicalWidth - leftMargin - printableWidth;
253 const qreal bottomMargin = physicalHeight - topMargin - printableHeight;
254 margins = QMarginsF(leftMargin, topMargin, rightMargin, bottomMargin);
255 if (separateDevMode)
256 free(devMode);
257 DeleteDC(pDC);
258 }
259 return margins;
260}
261
263{
264 const int resCount = DeviceCapabilities(wcharId(), nullptr, DC_ENUMRESOLUTIONS, nullptr, nullptr);
265 if (resCount > 0) {
266 QScopedArrayPointer<LONG> resolutions(new LONG[resCount*2]);
267 // Get the details and match the default paper size
268 if (DeviceCapabilities(wcharId(), nullptr, DC_ENUMRESOLUTIONS,
269 reinterpret_cast<LPWSTR>(resolutions.data()), nullptr) == resCount) {
270 for (int i = 0; i < int(resCount * 2); i += 2)
271 m_resolutions.append(resolutions[i+1]);
272 }
273 }
274 m_haveResolutions = true;
275 QWindowsPrinterInfo *info = windowsDeviceLookup()->data();
276 info[m_infoIndex].m_haveResolutions = true;
277 info[m_infoIndex].m_resolutions = m_resolutions;
278}
279
281{
282 int resolution = 72; // TODO Set a sensible default?
283
284 if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) {
285 // Get the default resolution
286 if (pDevMode->dmFields & DM_YRESOLUTION) {
287 if (pDevMode->dmPrintQuality > 0)
288 resolution = pDevMode->dmPrintQuality;
289 else
290 resolution = pDevMode->dmYResolution;
291 }
292 // Clean-up
293 free(pDevMode);
294 }
295 return resolution;
296}
297
299{
300 const auto printerId = wcharId();
301 const int binCount = DeviceCapabilities(printerId, nullptr, DC_BINS, nullptr, nullptr);
302 if (binCount > 0
303 && DeviceCapabilities(printerId, nullptr, DC_BINNAMES, nullptr, nullptr) == binCount) {
304
305 QScopedArrayPointer<WORD> bins(new WORD[binCount]);
306 QScopedArrayPointer<wchar_t> binNames(new wchar_t[binCount*24]);
307
308 // Get the details and match the default paper size
309 if (DeviceCapabilities(printerId, nullptr, DC_BINS,
310 reinterpret_cast<LPWSTR>(bins.data()), nullptr) == binCount
311 && DeviceCapabilities(printerId, nullptr, DC_BINNAMES, binNames.data(),
312 nullptr) == binCount) {
313
314 for (int i = 0; i < int(binCount); ++i) {
315 wchar_t *binName = binNames.data() + (i * 24);
316 QString name = QString::fromWCharArray(binName, qwcsnlen(binName, 24));
318 }
319
320 }
321 }
322
323 m_haveInputSlots = true;
324 QWindowsPrinterInfo *info = windowsDeviceLookup()->data();
325 info[m_infoIndex].m_haveInputSlots = true;
326 info[m_infoIndex].m_inputSlots = m_inputSlots;
327}
328
330{
332
333 if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) {
334 // Get the default input slot
335 if (pDevMode->dmFields & DM_DEFAULTSOURCE) {
336 QPrint::InputSlot tempSlot =
337 QPrintUtils::paperBinToInputSlot(pDevMode->dmDefaultSource, QString());
338 const auto inputSlots = supportedInputSlots();
339 for (const QPrint::InputSlot &slot : inputSlots) {
340 if (slot.key == tempSlot.key) {
341 inputSlot = slot;
342 break;
343 }
344 }
345 }
346 // Clean-up
347 free(pDevMode);
348 }
349 return inputSlot;
350}
351
353{
355 m_haveOutputBins = true;
356 QWindowsPrinterInfo *info = windowsDeviceLookup()->data();
357 info[m_infoIndex].m_haveOutputBins = true;
358 info[m_infoIndex].m_outputBins = m_outputBins;
359}
360
362{
364 DWORD duplex = DeviceCapabilities(wcharId(), nullptr, DC_DUPLEX, nullptr, nullptr);
365 if (int(duplex) == 1) {
366 // TODO Assume if duplex flag supports both modes
370 }
371 m_haveDuplexModes = true;
372 QWindowsPrinterInfo *info = windowsDeviceLookup()->data();
373 info[m_infoIndex].m_haveDuplexModes = true;
374 info[m_infoIndex].m_duplexModes = m_duplexModes;
375}
376
378{
380
381 if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) {
382 // Get the default duplex mode
383 if (pDevMode->dmFields & DM_DUPLEX) {
384 if (pDevMode->dmDuplex == DMDUP_VERTICAL)
385 duplexMode = QPrint::DuplexLongSide;
386 else if (pDevMode->dmDuplex == DMDUP_HORIZONTAL)
387 duplexMode = QPrint::DuplexShortSide;
388 }
389 // Clean-up
390 free(pDevMode);
391 }
392 return duplexMode;
393}
394
396{
398 DWORD color = DeviceCapabilities(wcharId(), nullptr, DC_COLORDEVICE, nullptr, nullptr);
399 if (int(color) == 1)
401 m_haveColorModes = true;
402 QWindowsPrinterInfo *info = windowsDeviceLookup()->data();
403 info[m_infoIndex].m_haveColorModes = true;
404 info[m_infoIndex].m_colorModes = m_colorModes;
405}
406
408{
409 if (!m_haveColorModes)
412 return QPrint::GrayScale;
413
415
416 if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) {
417 // Get the default color mode
418 if (pDevMode->dmFields & DM_COLOR && pDevMode->dmColor == DMCOLOR_COLOR)
419 colorMode = QPrint::Color;
420 // Clean-up
421 free(pDevMode);
422 }
423 return colorMode;
424}
425
427{
429 DWORD needed = 0;
430 DWORD returned = 0;
431 if ((!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 4, 0, 0, &needed, &returned)
432 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
433 || !needed) {
434 return list;
435 }
436 QScopedArrayPointer<BYTE> buffer(new BYTE[needed]);
437 if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 4, buffer.data(), needed, &needed, &returned))
438 return list;
439 PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer.data());
440 for (uint i = 0; i < returned; ++i)
441 list.append(QString::fromWCharArray(infoList[i].pPrinterName));
442 return list;
443}
444
446{
447 DWORD size = 0;
448 if (GetDefaultPrinter(nullptr, &size) == ERROR_FILE_NOT_FOUND || size < 2)
449 return QString();
450
451 QScopedArrayPointer<wchar_t> name(new wchar_t[size]);
452 GetDefaultPrinter(name.data(), &size);
453 return QString::fromWCharArray(name.data());
454}
455
457{
458 auto printerId = wcharId();
459 m_supportsMultipleCopies = (DeviceCapabilities(printerId, nullptr, DC_COPIES, nullptr, nullptr) > 1);
460 m_supportsCollateCopies = DeviceCapabilities(printerId, nullptr, DC_COLLATE, nullptr, nullptr);
461 m_haveCopies = true;
462 QWindowsPrinterInfo *info = windowsDeviceLookup()->data();
463 info[m_infoIndex].m_haveCopies = true;
464 info[m_infoIndex].m_supportsMultipleCopies = m_supportsMultipleCopies;
465 info[m_infoIndex].m_supportsCollateCopies = m_supportsCollateCopies;
466}
467
469{
470 if (!m_haveCopies)
473}
474
476{
477 if (!m_haveCopies)
480}
481
483{
484 if (!m_haveMinMaxPageSizes)
487}
488
490{
491 if (!m_haveMinMaxPageSizes)
494}
495
497{
498 if (!m_haveMinMaxPageSizes)
501}
502
504{
505 // Min/Max custom size is in tenths of a millimeter
507 auto printerId = wcharId();
508 DWORD min = DeviceCapabilities(printerId, nullptr, DC_MINEXTENT, nullptr, nullptr);
509 m_minimumPhysicalPageSize = QSize((LOWORD(min) / 10.0) * multiplier, (HIWORD(min) / 10.0) * multiplier);
510 DWORD max = DeviceCapabilities(printerId, nullptr, DC_MAXEXTENT, nullptr, nullptr);
511 m_maximumPhysicalPageSize = QSize((LOWORD(max) / 10.0) * multiplier, (HIWORD(max) / 10.0) * multiplier);
513 m_haveMinMaxPageSizes = true;
514 QWindowsPrinterInfo *info = windowsDeviceLookup()->data();
515 info[m_infoIndex].m_haveCopies = true;
516 info[m_infoIndex].m_supportsMultipleCopies = m_supportsMultipleCopies;
517 info[m_infoIndex].m_supportsCollateCopies = m_supportsCollateCopies;
518}
519
Definition qlist.h:75
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmargins.h:270
Unit
This enum type is used to specify the measurement unit for page layout and margins.
Definition qpagelayout.h:24
Orientation
This enum type defines the page orientation.
Definition qpagelayout.h:33
\inmodule QtGui
Definition qpagesize.h:22
int windowsId() const
Returns the Windows DMPAPER enum value for the page size.
QSizeF size(Unit units) const
Returns the size of the page in the required units.
PageSizeId id() const
Returns the standard QPageSize::PageSizeId of the page, or QPageSize::Custom.
static QPageSize createPageSize(const QString &key, const QSize &size, const QString &localizedName)
virtual QList< QPrint::InputSlot > supportedInputSlots() const
QList< QPrint::InputSlot > m_inputSlots
QList< QPrint::ColorMode > m_colorModes
QList< QPageSize > m_pageSizes
virtual QString name() const
virtual QPrint::InputSlot defaultInputSlot() const
QList< QPrint::OutputBin > m_outputBins
QList< QPrint::DuplexMode > m_duplexModes
virtual QPrint::OutputBin defaultOutputBin() const
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:332
constexpr qreal height() const noexcept
Returns the height.
Definition qsize.h:335
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
void loadColorModes() const override
void loadInputSlots() const override
void loadOutputBins() const override
void loadResolutions() const override
static QString defaultPrintDeviceId()
QPrint::InputSlot defaultInputSlot() const override
QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation, int resolution) const override
bool isDefault() const override
bool isValid() const override
QPrint::DeviceState state() const override
int defaultResolution() const override
void loadDuplexModes() const override
QSize maximumPhysicalPageSize() const override
bool supportsCollateCopies() const override
QSize minimumPhysicalPageSize() const override
bool supportsMultipleCopies() const override
QPrint::ColorMode defaultColorMode() const override
QPageSize defaultPageSize() const override
QPrint::DuplexMode defaultDuplexMode() const override
bool supportsCustomPageSizes() const override
static QStringList availablePrintDeviceIds()
void loadPageSizes() const override
QList< QPrint::OutputBin > m_outputBins
QList< QPrint::DuplexMode > m_duplexModes
QList< QPrint::InputSlot > m_inputSlots
QList< QPrint::ColorMode > m_colorModes
QList< QPageSize > m_pageSizes
QString str
[2]
QPrint::InputSlot paperBinToInputSlot(int windowsId, const QString &name)
Definition qprint.cpp:87
ColorMode
Definition qprint_p.h:72
@ Color
Definition qprint_p.h:74
@ GrayScale
Definition qprint_p.h:73
DuplexMode
Definition qprint_p.h:64
@ DuplexLongSide
Definition qprint_p.h:67
@ DuplexShortSide
Definition qprint_p.h:68
@ DuplexNone
Definition qprint_p.h:65
@ DuplexAuto
Definition qprint_p.h:66
DeviceState
Definition qprint_p.h:56
@ Idle
Definition qprint_p.h:57
@ Error
Definition qprint_p.h:60
@ Active
Definition qprint_p.h:58
Combined button and popup list for selecting options.
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLint GLint GLint GLint GLint x
[0]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLenum GLuint id
[7]
GLenum GLuint buffer
GLuint color
[2]
GLuint name
GLint y
GLuint64EXT * result
[6]
QT_BEGIN_NAMESPACE Q_GUI_EXPORT qreal qt_pointMultiplier(QPageLayout::Unit unit)
@ DMPAPER_LAST
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
#define DC_COLLATE
qreal qt_pointMultiplier(QPageLayout::Unit unit)
static LPDEVMODE getDevmode(HANDLE hPrinter, const QString &printerId)
static uint qwcsnlen(const wchar_t *str, uint maxlen)
QList< int > list
[14]
QHostInfo info
[0]
bool contains(const AT &t) const noexcept
Definition qlist.h:45