Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2014 John Layt <jlayt@kde.org>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qcocoaprintdevice.h"
41
42#if QT_CONFIG(mimetype)
43#include <QtCore/qmimedatabase.h>
44#endif
45#include <qdebug.h>
46
47QT_BEGIN_NAMESPACE
48
49#ifndef QT_NO_PRINTER
50
51// The CUPS PPD APIs were deprecated in CUPS 1.6/macOS 10.8, but
52// as long as we're supporting RHEL 6, which still ships CUPS 1.4
53// we're not going to rewrite this, as we want to share the code
54// between macOS and Linux for the CUPS-bits. See discussion in
55// https://bugreports.qt.io/browse/QTBUG-56545
56#pragma message "Disabling CUPS PPD deprecation warnings. This should be fixed once we drop support for RHEL6 (QTBUG-56545)"
57#pragma clang diagnostic push
58#pragma clang diagnostic ignored "-Wdeprecated-declarations"
59
60static QPrint::DuplexMode macToDuplexMode(const PMDuplexMode &mode)
61{
62 if (mode == kPMDuplexTumble)
63 return QPrint::DuplexShortSide;
64 else if (mode == kPMDuplexNoTumble)
65 return QPrint::DuplexLongSide;
66 else // kPMDuplexNone or kPMSimplexTumble
67 return QPrint::DuplexNone;
68}
69
70QCocoaPrintDevice::QCocoaPrintDevice()
71 : QPlatformPrintDevice(),
72 m_printer(nullptr),
73 m_session(nullptr),
74 m_ppd(nullptr)
75{
76}
77
78QCocoaPrintDevice::QCocoaPrintDevice(const QString &id)
79 : QPlatformPrintDevice(id),
80 m_printer(nullptr),
81 m_session(nullptr),
82 m_ppd(nullptr)
83{
84 if (!id.isEmpty()) {
85 m_printer = PMPrinterCreateFromPrinterID(id.toCFString());
86 if (m_printer) {
87 m_name = QString::fromCFString(PMPrinterGetName(m_printer));
88 m_location = QString::fromCFString(PMPrinterGetLocation(m_printer));
89 CFStringRef cfMakeAndModel;
90 if (PMPrinterGetMakeAndModelName(m_printer, &cfMakeAndModel) == noErr)
91 m_makeAndModel = QString::fromCFString(cfMakeAndModel);
92 Boolean isRemote;
93 if (PMPrinterIsRemote(m_printer, &isRemote) == noErr)
94 m_isRemote = isRemote;
95 if (PMCreateSession(&m_session) == noErr)
96 PMSessionSetCurrentPMPrinter(m_session, m_printer);
97
98 // No native api to query these options, need to use PPD directly, note is deprecated from 1.6 onwards
99 if (openPpdFile()) {
100 // Note this is if the hardware does multiple copies, not if Cups can
101 m_supportsMultipleCopies = !m_ppd->manual_copies;
102 // Note this is if the hardware does collation, not if Cups can
103 ppd_option_t *collate = ppdFindOption(m_ppd, "Collate");
104 if (collate)
105 m_supportsCollateCopies = true;
106 m_supportsCustomPageSizes = m_ppd->custom_max[0] > 0 && m_ppd->custom_max[1] > 0;
107 m_minimumPhysicalPageSize = QSize(m_ppd->custom_min[0], m_ppd->custom_min[1]);
108 m_maximumPhysicalPageSize = QSize(m_ppd->custom_max[0], m_ppd->custom_max[1]);
109 m_customMargins = QMarginsF(m_ppd->custom_margins[0], m_ppd->custom_margins[3],
110 m_ppd->custom_margins[2], m_ppd->custom_margins[1]);
111 }
112 }
113 }
114}
115
116QCocoaPrintDevice::~QCocoaPrintDevice()
117{
118 if (m_ppd)
119 ppdClose(m_ppd);
120 foreach (PMPaper paper, m_macPapers)
121 PMRelease(paper);
122 // Releasing the session appears to also release the printer
123 if (m_session)
124 PMRelease(m_session);
125 else if (m_printer)
126 PMRelease(m_printer);
127}
128
129bool QCocoaPrintDevice::isValid() const
130{
131 return m_printer ? true : false;
132}
133
134bool QCocoaPrintDevice::isDefault() const
135{
136 return PMPrinterIsDefault(m_printer);
137}
138
139QPrint::DeviceState QCocoaPrintDevice::state() const
140{
141 PMPrinterState state;
142 if (PMPrinterGetState(m_printer, &state) == noErr) {
143 if (state == kPMPrinterIdle)
144 return QPrint::Idle;
145 else if (state == kPMPrinterProcessing)
146 return QPrint::Active;
147 else if (state == kPMPrinterStopped)
148 return QPrint::Error;
149 }
150 return QPrint::Error;
151}
152
153QPageSize QCocoaPrintDevice::createPageSize(const PMPaper &paper) const
154{
155 CFStringRef key;
156 double width;
157 double height;
158 CFStringRef localizedName;
159 if (PMPaperGetPPDPaperName(paper, &key) == noErr
160 && PMPaperGetWidth(paper, &width) == noErr
161 && PMPaperGetHeight(paper, &height) == noErr
162 && PMPaperCreateLocalizedName(paper, m_printer, &localizedName) == noErr) {
163 QPageSize pageSize = QPlatformPrintDevice::createPageSize(QString::fromCFString(key),QSize(width, height),
164 QString::fromCFString(localizedName));
165 CFRelease(localizedName);
166 return pageSize;
167 }
168 return QPageSize();
169}
170
171void QCocoaPrintDevice::loadPageSizes() const
172{
173 m_pageSizes.clear();
174 foreach (PMPaper paper, m_macPapers)
175 PMRelease(paper);
176 m_macPapers.clear();
177 m_printableMargins.clear();
178 CFArrayRef paperSizes;
179 if (PMPrinterGetPaperList(m_printer, &paperSizes) == noErr) {
180 int count = CFArrayGetCount(paperSizes);
181 for (int i = 0; i < count; ++i) {
182 PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(paperSizes, i)));
183 QPageSize pageSize = createPageSize(paper);
184 if (pageSize.isValid()) {
185 m_pageSizes.append(pageSize);
186 PMRetain(paper);
187 m_macPapers.insert(pageSize.key(), paper);
188 PMPaperMargins printMargins;
189 PMPaperGetMargins(paper, &printMargins);
190 m_printableMargins.insert(pageSize.key(), QMarginsF(printMargins.left, printMargins.top,
191 printMargins.right, printMargins.bottom));
192 }
193 }
194 }
195 m_havePageSizes = true;
196}
197
198QPageSize QCocoaPrintDevice::defaultPageSize() const
199{
200 QPageSize pageSize;
201 PMPageFormat pageFormat;
202 PMPaper paper;
203 if (PMCreatePageFormat(&pageFormat) == noErr) {
204 if (PMSessionDefaultPageFormat(m_session, pageFormat) == noErr
205 && PMGetPageFormatPaper(pageFormat, &paper) == noErr) {
206 pageSize = createPageSize(paper);
207 }
208 PMRelease(pageFormat);
209 }
210 return pageSize;
211}
212
213QMarginsF QCocoaPrintDevice::printableMargins(const QPageSize &pageSize,
214 QPageLayout::Orientation orientation,
215 int resolution) const
216{
217 Q_UNUSED(orientation)
218 Q_UNUSED(resolution)
219 if (!m_havePageSizes)
220 loadPageSizes();
221 if (m_printableMargins.contains(pageSize.key()))
222 return m_printableMargins.value(pageSize.key());
223 return m_customMargins;
224}
225
226void QCocoaPrintDevice::loadResolutions() const
227{
228 m_resolutions.clear();
229 UInt32 count;
230 if (PMPrinterGetPrinterResolutionCount(m_printer, &count) == noErr) {
231 // 1-based index
232 for (UInt32 i = 1; i <= count; ++i) {
233 PMResolution resolution;
234 if (PMPrinterGetIndexedPrinterResolution(m_printer, i, &resolution) == noErr)
235 m_resolutions.append(int(resolution.hRes));
236 }
237 }
238 m_haveResolutions = true;
239}
240
241int QCocoaPrintDevice::defaultResolution() const
242{
243 int defaultResolution = 72;
244 PMPrintSettings settings;
245 if (PMCreatePrintSettings(&settings) == noErr) {
246 PMResolution resolution;
247 if (PMSessionDefaultPrintSettings(m_session, settings) == noErr
248 && PMPrinterGetOutputResolution(m_printer, settings, &resolution) == noErr) {
249 // PMPrinterGetOutputResolution usually fails with -9589 kPMKeyNotFound as not set in PPD
250 defaultResolution = int(resolution.hRes);
251 }
252 PMRelease(settings);
253 }
254 // If no value returned (usually means not set in PPD) then use supported resolutions which
255 // OSX will have populated with at least one default value (but why not returned by call?)
256 if (defaultResolution <= 0) {
257 if (!m_haveResolutions)
258 loadResolutions();
259 if (m_resolutions.count() > 0)
260 return m_resolutions.at(0); // First value or highest? Only likely to be one anyway.
261 return 72; // TDOD More sensible default value???
262 }
263 return defaultResolution;
264}
265
266void QCocoaPrintDevice::loadInputSlots() const
267{
268 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
269 // TODO Deal with concatenated names like Tray1Manual or Tray1_Man,
270 // will currently show as CustomInputSlot
271 // TODO Deal with separate ManualFeed key
272 // Try load standard PPD options first
273 m_inputSlots.clear();
274 if (m_ppd) {
275 ppd_option_t *inputSlots = ppdFindOption(m_ppd, "InputSlot");
276 if (inputSlots) {
277 for (int i = 0; i < inputSlots->num_choices; ++i)
278 m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[i]));
279 }
280 // If no result, try just the default
281 if (m_inputSlots.size() == 0) {
282 inputSlots = ppdFindOption(m_ppd, "DefaultInputSlot");
283 if (inputSlots)
284 m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[0]));
285 }
286 }
287 // If still no result, just use Auto
288 if (m_inputSlots.size() == 0)
289 m_inputSlots.append(QPlatformPrintDevice::defaultInputSlot());
290 m_haveInputSlots = true;
291}
292
293QPrint::InputSlot QCocoaPrintDevice::defaultInputSlot() const
294{
295 // No native api to query, use PPD directly
296 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
297 // Try load standard PPD option first
298 if (m_ppd) {
299 ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultInputSlot");
300 if (inputSlot)
301 return QPrintUtils::ppdChoiceToInputSlot(inputSlot->choices[0]);
302 // If no result, then try a marked option
303 ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "InputSlot");
304 if (defaultChoice)
305 return QPrintUtils::ppdChoiceToInputSlot(*defaultChoice);
306 }
307 // Otherwise return Auto
308 return QPlatformPrintDevice::defaultInputSlot();
309}
310
311void QCocoaPrintDevice::loadOutputBins() const
312{
313 // No native api to query, use PPD directly
314 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
315 m_outputBins.clear();
316 if (m_ppd) {
317 ppd_option_t *outputBins = ppdFindOption(m_ppd, "OutputBin");
318 if (outputBins) {
319 for (int i = 0; i < outputBins->num_choices; ++i)
320 m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[i]));
321 }
322 // If no result, try just the default
323 if (m_outputBins.size() == 0) {
324 outputBins = ppdFindOption(m_ppd, "DefaultOutputBin");
325 if (outputBins)
326 m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[0]));
327 }
328 }
329 // If still no result, just use Auto
330 if (m_outputBins.size() == 0)
331 m_outputBins.append(QPlatformPrintDevice::defaultOutputBin());
332 m_haveOutputBins = true;
333}
334
335QPrint::OutputBin QCocoaPrintDevice::defaultOutputBin() const
336{
337 // No native api to query, use PPD directly
338 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
339 // Try load standard PPD option first
340 if (m_ppd) {
341 ppd_option_t *outputBin = ppdFindOption(m_ppd, "DefaultOutputBin");
342 if (outputBin)
343 return QPrintUtils::ppdChoiceToOutputBin(outputBin->choices[0]);
344 // If no result, then try a marked option
345 ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "OutputBin");
346 if (defaultChoice)
347 return QPrintUtils::ppdChoiceToOutputBin(*defaultChoice);
348 }
349 // Otherwise return AutoBin
350 return QPlatformPrintDevice::defaultOutputBin();
351}
352
353void QCocoaPrintDevice::loadDuplexModes() const
354{
355 // No native api to query, use PPD directly
356 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
357 // Try load standard PPD options first
358 m_duplexModes.clear();
359 if (m_ppd) {
360 ppd_option_t *duplexModes = ppdFindOption(m_ppd, "Duplex");
361 if (duplexModes) {
362 for (int i = 0; i < duplexModes->num_choices; ++i)
363 m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[i].choice));
364 }
365 // If no result, try just the default
366 if (m_duplexModes.size() == 0) {
367 duplexModes = ppdFindOption(m_ppd, "DefaultDuplex");
368 if (duplexModes)
369 m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[0].choice));
370 }
371 }
372 // If still no result, or not added in PPD, then add None
373 if (m_duplexModes.size() == 0 || !m_duplexModes.contains(QPrint::DuplexNone))
374 m_duplexModes.append(QPrint::DuplexNone);
375 // If have both modes, then can support DuplexAuto
376 if (m_duplexModes.contains(QPrint::DuplexLongSide) && m_duplexModes.contains(QPrint::DuplexShortSide))
377 m_duplexModes.append(QPrint::DuplexAuto);
378 m_haveDuplexModes = true;
379}
380
381QPrint::DuplexMode QCocoaPrintDevice::defaultDuplexMode() const
382{
383 QPrint::DuplexMode defaultMode = QPrint::DuplexNone;
384 PMPrintSettings settings;
385 if (PMCreatePrintSettings(&settings) == noErr) {
386 PMDuplexMode duplexMode;
387 if (PMSessionDefaultPrintSettings(m_session, settings) == noErr
388 && PMGetDuplex(settings, &duplexMode) == noErr) {
389 defaultMode = macToDuplexMode(duplexMode);
390 }
391 PMRelease(settings);
392 }
393 return defaultMode;
394}
395
396void QCocoaPrintDevice::loadColorModes() const
397{
398 // No native api to query, use PPD directly
399 m_colorModes.clear();
400 m_colorModes.append(QPrint::GrayScale);
401 if (!m_ppd || (m_ppd && m_ppd->color_device))
402 m_colorModes.append(QPrint::Color);
403 m_haveColorModes = true;
404}
405
406QPrint::ColorMode QCocoaPrintDevice::defaultColorMode() const
407{
408 // No native api to query, use PPD directly
409 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
410 // Not a proper option, usually only know if supports color or not, but some
411 // users known to abuse ColorModel to always force GrayScale.
412 if (m_ppd && supportedColorModes().contains(QPrint::Color)) {
413 ppd_option_t *colorModel = ppdFindOption(m_ppd, "DefaultColorModel");
414 if (!colorModel)
415 colorModel = ppdFindOption(m_ppd, "ColorModel");
416 if (!colorModel || qstrcmp(colorModel->defchoice, "Gray") != 0)
417 return QPrint::Color;
418 }
419 return QPrint::GrayScale;
420}
421
422#if QT_CONFIG(mimetype)
423void QCocoaPrintDevice::loadMimeTypes() const
424{
425 // TODO Check how settings affect returned list
426 m_mimeTypes.clear();
427 QMimeDatabase db;
428 PMPrintSettings settings;
429 if (PMCreatePrintSettings(&settings) == noErr) {
430 CFArrayRef mimeTypes;
431 if (PMPrinterGetMimeTypes(m_printer, settings, &mimeTypes) == noErr) {
432 int count = CFArrayGetCount(mimeTypes);
433 for (int i = 0; i < count; ++i) {
434 CFStringRef mimeName = static_cast<CFStringRef>(const_cast<void *>(CFArrayGetValueAtIndex(mimeTypes, i)));
435 QMimeType mimeType = db.mimeTypeForName(QString::fromCFString(mimeName));
436 if (mimeType.isValid())
437 m_mimeTypes.append(mimeType);
438 }
439 }
440 PMRelease(settings);
441 }
442 m_haveMimeTypes = true;
443}
444#endif // mimetype
445
446bool QCocoaPrintDevice::openPpdFile()
447{
448 if (m_ppd)
449 ppdClose(m_ppd);
450 m_ppd = nullptr;
451 CFURLRef ppdURL = nullptr;
452 char ppdPath[MAXPATHLEN];
453 if (PMPrinterCopyDescriptionURL(m_printer, kPMPPDDescriptionType, &ppdURL) == noErr
454 && ppdURL) {
455 if (CFURLGetFileSystemRepresentation(ppdURL, true, (UInt8*)ppdPath, sizeof(ppdPath)))
456 m_ppd = ppdOpenFile(ppdPath);
457 CFRelease(ppdURL);
458 }
459 return m_ppd ? true : false;
460}
461
462PMPrinter QCocoaPrintDevice::macPrinter() const
463{
464 return m_printer;
465}
466
467// Returns a cached printer PMPaper, or creates and caches a new custom PMPaper
468// Caller should never release a cached PMPaper!
469PMPaper QCocoaPrintDevice::macPaper(const QPageSize &pageSize) const
470{
471 if (!m_havePageSizes)
472 loadPageSizes();
473 // If keys match, then is a supported size or an existing custom size
474 if (m_macPapers.contains(pageSize.key()))
475 return m_macPapers.value(pageSize.key());
476 // For any other page size, whether custom or just unsupported, needs to be a custom PMPaper
477 PMPaper paper = nullptr;
478 PMPaperMargins paperMargins;
479 paperMargins.left = m_customMargins.left();
480 paperMargins.right = m_customMargins.right();
481 paperMargins.top = m_customMargins.top();
482 paperMargins.bottom = m_customMargins.bottom();
483 PMPaperCreateCustom(m_printer, QCFString(pageSize.key()), QCFString(pageSize.name()),
484 pageSize.sizePoints().width(), pageSize.sizePoints().height(),
485 &paperMargins, &paper);
486 m_macPapers.insert(pageSize.key(), paper);
487 return paper;
488}
489
490#pragma clang diagnostic pop
491
492#endif // QT_NO_PRINTER
493
494QT_END_NAMESPACE
495

Warning: That file was not part of the compilation database. It may have many parsing errors.