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
qcocoaprintdevice.mm
Go to the documentation of this file.
1// Copyright (C) 2014 John Layt <jlayt@kde.org>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <ApplicationServices/ApplicationServices.h>
5
7
8#if QT_CONFIG(mimetype)
9#include <QtCore/qmimedatabase.h>
10#endif
11#include <qdebug.h>
12
13#include <QtCore/private/qcore_mac_p.h>
14
16
17#ifndef QT_NO_PRINTER
18
19// The CUPS PPD APIs were deprecated in CUPS 1.6/macOS 10.8, but
20// the replacement APIs are unfortunately not sufficient. See:
21// https://bugreports.qt.io/browse/QTBUG-56545
22#pragma clang diagnostic push
23#pragma clang diagnostic ignored "-Wdeprecated-declarations"
24
25static QPrint::DuplexMode macToDuplexMode(const PMDuplexMode &mode)
26{
27 if (mode == kPMDuplexTumble)
29 else if (mode == kPMDuplexNoTumble)
31 else // kPMDuplexNone or kPMSimplexTumble
32 return QPrint::DuplexNone;
33}
34
42
45 m_printer(nullptr),
47 m_ppd(nullptr)
48{
49 if (!id.isEmpty()) {
50 m_printer = PMPrinterCreateFromPrinterID(id.toCFString());
51 if (m_printer) {
52 m_name = QString::fromCFString(PMPrinterGetName(m_printer));
53 m_location = QString::fromCFString(PMPrinterGetLocation(m_printer));
54 CFStringRef cfMakeAndModel;
55 if (PMPrinterGetMakeAndModelName(m_printer, &cfMakeAndModel) == noErr)
56 m_makeAndModel = QString::fromCFString(cfMakeAndModel);
57 Boolean isRemote;
58 if (PMPrinterIsRemote(m_printer, &isRemote) == noErr)
60 if (PMCreateSession(&m_session) == noErr)
61 PMSessionSetCurrentPMPrinter(m_session, m_printer);
62
63 // No native api to query these options, need to use PPD directly, note is deprecated from 1.6 onwards
64 if (openPpdFile()) {
65 // Note this is if the hardware does multiple copies, not if Cups can
66 m_supportsMultipleCopies = !m_ppd->manual_copies;
67 // Note this is if the hardware does collation, not if Cups can
68 ppd_option_t *collate = ppdFindOption(m_ppd, "Collate");
69 if (collate)
71 m_supportsCustomPageSizes = m_ppd->custom_max[0] > 0 && m_ppd->custom_max[1] > 0;
72 m_minimumPhysicalPageSize = QSize(m_ppd->custom_min[0], m_ppd->custom_min[1]);
73 m_maximumPhysicalPageSize = QSize(m_ppd->custom_max[0], m_ppd->custom_max[1]);
74 m_customMargins = QMarginsF(m_ppd->custom_margins[0], m_ppd->custom_margins[3],
75 m_ppd->custom_margins[2], m_ppd->custom_margins[1]);
76 }
77 }
78 }
79}
80
82{
83 if (m_ppd)
84 ppdClose(m_ppd);
85 for (PMPaper paper : m_macPapers)
86 PMRelease(paper);
87 // Releasing the session appears to also release the printer
88 if (m_session)
89 PMRelease(m_session);
90 else if (m_printer)
91 PMRelease(m_printer);
92}
93
95{
96 return m_printer ? true : false;
97}
98
100{
101 return PMPrinterIsDefault(m_printer);
102}
103
105{
106 PMPrinterState state;
107 if (PMPrinterGetState(m_printer, &state) == noErr) {
108 if (state == kPMPrinterIdle)
109 return QPrint::Idle;
110 else if (state == kPMPrinterProcessing)
111 return QPrint::Active;
112 else if (state == kPMPrinterStopped)
113 return QPrint::Error;
114 }
115 return QPrint::Error;
116}
117
118QPageSize QCocoaPrintDevice::createPageSize(const PMPaper &paper) const
119{
120 CFStringRef key;
121 double width;
122 double height;
123 CFStringRef localizedName;
124 if (PMPaperGetPPDPaperName(paper, &key) == noErr
125 && PMPaperGetWidth(paper, &width) == noErr
126 && PMPaperGetHeight(paper, &height) == noErr
127 && PMPaperCreateLocalizedName(paper, m_printer, &localizedName) == noErr) {
128 QPageSize pageSize = QPlatformPrintDevice::createPageSize(QString::fromCFString(key),QSize(width, height),
129 QString::fromCFString(localizedName));
130 CFRelease(localizedName);
131 return pageSize;
132 }
133 return QPageSize();
134}
135
137{
139 for (PMPaper paper : m_macPapers)
140 PMRelease(paper);
141 m_macPapers.clear();
142 m_printableMargins.clear();
143 CFArrayRef paperSizes;
144 if (PMPrinterGetPaperList(m_printer, &paperSizes) == noErr) {
145 int count = CFArrayGetCount(paperSizes);
146 for (int i = 0; i < count; ++i) {
147 PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(paperSizes, i)));
148 QPageSize pageSize = createPageSize(paper);
149 if (pageSize.isValid()) {
150 m_pageSizes.append(pageSize);
151 PMRetain(paper);
152 m_macPapers.insert(pageSize.key(), paper);
153 PMPaperMargins printMargins;
154 PMPaperGetMargins(paper, &printMargins);
155 m_printableMargins.insert(pageSize.key(), QMarginsF(printMargins.left, printMargins.top,
156 printMargins.right, printMargins.bottom));
157 }
158 }
159 }
160 m_havePageSizes = true;
161}
162
164{
165 QPageSize pageSize;
166 PMPageFormat pageFormat;
167 PMPaper paper;
168 if (PMCreatePageFormat(&pageFormat) == noErr) {
169 if (PMSessionDefaultPageFormat(m_session, pageFormat) == noErr
170 && PMGetPageFormatPaper(pageFormat, &paper) == noErr) {
171 pageSize = createPageSize(paper);
172 }
173 PMRelease(pageFormat);
174 }
175 return pageSize;
176}
177
179 QPageLayout::Orientation orientation,
180 int resolution) const
181{
182 Q_UNUSED(orientation);
183 Q_UNUSED(resolution);
184 if (!m_havePageSizes)
186 if (m_printableMargins.contains(pageSize.key()))
187 return m_printableMargins.value(pageSize.key());
188 return m_customMargins;
189}
190
192{
194 UInt32 count;
195 if (PMPrinterGetPrinterResolutionCount(m_printer, &count) == noErr) {
196 // 1-based index
197 for (UInt32 i = 1; i <= count; ++i) {
198 PMResolution resolution;
199 if (PMPrinterGetIndexedPrinterResolution(m_printer, i, &resolution) == noErr)
200 m_resolutions.append(int(resolution.hRes));
201 }
202 }
203 m_haveResolutions = true;
204}
205
207{
208 int defaultResolution = 72;
209 PMPrintSettings settings;
210 if (PMCreatePrintSettings(&settings) == noErr) {
211 PMResolution resolution;
212 if (PMSessionDefaultPrintSettings(m_session, settings) == noErr
213 && PMPrinterGetOutputResolution(m_printer, settings, &resolution) == noErr) {
214 // PMPrinterGetOutputResolution usually fails with -9589 kPMKeyNotFound as not set in PPD
215 defaultResolution = int(resolution.hRes);
216 }
217 PMRelease(settings);
218 }
219 // If no value returned (usually means not set in PPD) then use supported resolutions which
220 // OSX will have populated with at least one default value (but why not returned by call?)
221 if (defaultResolution <= 0) {
224 if (m_resolutions.count() > 0)
225 return m_resolutions.at(0); // First value or highest? Only likely to be one anyway.
226 return 72; // TDOD More sensible default value???
227 }
228 return defaultResolution;
229}
230
232{
233 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
234 // TODO Deal with concatenated names like Tray1Manual or Tray1_Man,
235 // will currently show as CustomInputSlot
236 // TODO Deal with separate ManualFeed key
237 // Try load standard PPD options first
239 if (m_ppd) {
240 ppd_option_t *inputSlots = ppdFindOption(m_ppd, "InputSlot");
241 if (inputSlots) {
242 for (int i = 0; i < inputSlots->num_choices; ++i)
243 m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[i]));
244 }
245 // If no result, try just the default
246 if (m_inputSlots.size() == 0) {
247 inputSlots = ppdFindOption(m_ppd, "DefaultInputSlot");
248 if (inputSlots)
249 m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[0]));
250 }
251 }
252 // If still no result, just use Auto
253 if (m_inputSlots.size() == 0)
255 m_haveInputSlots = true;
256}
257
259{
260 // No native api to query, use PPD directly
261 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
262 // Try load standard PPD option first
263 if (m_ppd) {
264 ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultInputSlot");
265 if (inputSlot)
266 return QPrintUtils::ppdChoiceToInputSlot(inputSlot->choices[0]);
267 // If no result, then try a marked option
268 ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "InputSlot");
269 if (defaultChoice)
270 return QPrintUtils::ppdChoiceToInputSlot(*defaultChoice);
271 }
272 // Otherwise return Auto
274}
275
277{
278 // No native api to query, use PPD directly
279 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
281 if (m_ppd) {
282 ppd_option_t *outputBins = ppdFindOption(m_ppd, "OutputBin");
283 if (outputBins) {
284 for (int i = 0; i < outputBins->num_choices; ++i)
285 m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[i]));
286 }
287 // If no result, try just the default
288 if (m_outputBins.size() == 0) {
289 outputBins = ppdFindOption(m_ppd, "DefaultOutputBin");
290 if (outputBins)
291 m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[0]));
292 }
293 }
294 // If still no result, just use Auto
295 if (m_outputBins.size() == 0)
297 m_haveOutputBins = true;
298}
299
301{
302 // No native api to query, use PPD directly
303 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
304 // Try load standard PPD option first
305 if (m_ppd) {
306 ppd_option_t *outputBin = ppdFindOption(m_ppd, "DefaultOutputBin");
307 if (outputBin)
308 return QPrintUtils::ppdChoiceToOutputBin(outputBin->choices[0]);
309 // If no result, then try a marked option
310 ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "OutputBin");
311 if (defaultChoice)
312 return QPrintUtils::ppdChoiceToOutputBin(*defaultChoice);
313 }
314 // Otherwise return AutoBin
316}
317
319{
320 // No native api to query, use PPD directly
321 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
322 // Try load standard PPD options first
324 if (m_ppd) {
325 ppd_option_t *duplexModes = ppdFindOption(m_ppd, "Duplex");
326 if (duplexModes) {
327 for (int i = 0; i < duplexModes->num_choices; ++i)
328 m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[i].choice));
329 }
330 // If no result, try just the default
331 if (m_duplexModes.size() == 0) {
332 duplexModes = ppdFindOption(m_ppd, "DefaultDuplex");
333 if (duplexModes)
334 m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[0].choice));
335 }
336 }
337 // If still no result, or not added in PPD, then add None
340 // If have both modes, then can support DuplexAuto
343 m_haveDuplexModes = true;
344}
345
347{
349 PMPrintSettings settings;
350 if (PMCreatePrintSettings(&settings) == noErr) {
351 PMDuplexMode duplexMode;
352 if (PMSessionDefaultPrintSettings(m_session, settings) == noErr
353 && PMGetDuplex(settings, &duplexMode) == noErr) {
354 defaultMode = macToDuplexMode(duplexMode);
355 }
356 PMRelease(settings);
357 }
358 return defaultMode;
359}
360
362{
363 // No native api to query, use PPD directly
366 if (!m_ppd || (m_ppd && m_ppd->color_device))
368 m_haveColorModes = true;
369}
370
372{
373 // No native api to query, use PPD directly
374 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
375 // Not a proper option, usually only know if supports color or not, but some
376 // users known to abuse ColorModel to always force GrayScale.
378 ppd_option_t *colorModel = ppdFindOption(m_ppd, "DefaultColorModel");
379 if (!colorModel)
380 colorModel = ppdFindOption(m_ppd, "ColorModel");
381 if (!colorModel || qstrcmp(colorModel->defchoice, "Gray") != 0)
382 return QPrint::Color;
383 }
384 return QPrint::GrayScale;
385}
386
387#if QT_CONFIG(mimetype)
388void QCocoaPrintDevice::loadMimeTypes() const
389{
390 // TODO Check how settings affect returned list
391 m_mimeTypes.clear();
393 PMPrintSettings settings;
394 if (PMCreatePrintSettings(&settings) == noErr) {
395 CFArrayRef mimeTypes;
396 if (PMPrinterGetMimeTypes(m_printer, settings, &mimeTypes) == noErr) {
397 int count = CFArrayGetCount(mimeTypes);
398 for (int i = 0; i < count; ++i) {
399 CFStringRef mimeName = static_cast<CFStringRef>(const_cast<void *>(CFArrayGetValueAtIndex(mimeTypes, i)));
400 QMimeType mimeType = db.mimeTypeForName(QString::fromCFString(mimeName));
401 if (mimeType.isValid())
402 m_mimeTypes.append(mimeType);
403 }
404 }
405 PMRelease(settings);
406 }
407 m_haveMimeTypes = true;
408}
409#endif // mimetype
410
411bool QCocoaPrintDevice::openPpdFile()
412{
413 if (m_ppd)
414 ppdClose(m_ppd);
415 m_ppd = nullptr;
416 CFURLRef ppdURL = nullptr;
417 char ppdPath[MAXPATHLEN];
418 if (PMPrinterCopyDescriptionURL(m_printer, kPMPPDDescriptionType, &ppdURL) == noErr
419 && ppdURL) {
420 if (CFURLGetFileSystemRepresentation(ppdURL, true, (UInt8*)ppdPath, sizeof(ppdPath)))
421 m_ppd = ppdOpenFile(ppdPath);
422 CFRelease(ppdURL);
423 }
424 return m_ppd ? true : false;
425}
426
428{
429 return m_printer;
430}
431
432// Returns a cached printer PMPaper, or creates and caches a new custom PMPaper
433// Caller should never release a cached PMPaper!
434PMPaper QCocoaPrintDevice::macPaper(const QPageSize &pageSize) const
435{
436 if (!m_havePageSizes)
438 // If keys match, then is a supported size or an existing custom size
439 if (m_macPapers.contains(pageSize.key()))
440 return m_macPapers.value(pageSize.key());
441 // For any other page size, whether custom or just unsupported, needs to be a custom PMPaper
442 PMPaper paper = nullptr;
443 PMPaperMargins paperMargins;
444 paperMargins.left = m_customMargins.left();
445 paperMargins.right = m_customMargins.right();
446 paperMargins.top = m_customMargins.top();
447 paperMargins.bottom = m_customMargins.bottom();
448 PMPaperCreateCustom(m_printer, QCFString(pageSize.key()), QCFString(pageSize.name()),
449 pageSize.sizePoints().width(), pageSize.sizePoints().height(),
450 &paperMargins, &paper);
451 m_macPapers.insert(pageSize.key(), paper);
452 return paper;
453}
454
455#pragma clang diagnostic pop
456
457#endif // QT_NO_PRINTER
458
AVFCameraSession * m_session
int defaultResolution() const override
QPrint::OutputBin defaultOutputBin() const override
void loadResolutions() const override
QPageSize defaultPageSize() const override
void loadOutputBins() const override
void loadColorModes() const override
QPrint::DeviceState state() const override
PMPrinter macPrinter() const
QPrint::DuplexMode defaultDuplexMode() const override
QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation, int resolution) const override
void loadDuplexModes() const override
QPrint::InputSlot defaultInputSlot() const override
bool isDefault() const override
PMPaper macPaper(const QPageSize &pageSize) const
bool isValid() const override
QPrint::ColorMode defaultColorMode() const override
void loadPageSizes() const override
void loadInputSlots() const override
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
T value(const Key &key) const noexcept
Definition qhash.h:1054
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
qsizetype size() const noexcept
Definition qlist.h:397
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
qsizetype count() const noexcept
Definition qlist.h:398
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
\inmodule QtCore
Definition qmargins.h:270
constexpr qreal right() const noexcept
Returns the right margin.
Definition qmargins.h:383
constexpr qreal left() const noexcept
Returns the left margin.
Definition qmargins.h:377
constexpr qreal top() const noexcept
Returns the top margin.
Definition qmargins.h:380
constexpr qreal bottom() const noexcept
Returns the bottom margin.
Definition qmargins.h:386
\inmodule QtCore
QMimeType mimeTypeForName(const QString &nameOrAlias) const
Returns a MIME type for nameOrAlias or an invalid one if none found.
\inmodule QtCore
Definition qmimetype.h:25
Orientation
This enum type defines the page orientation.
Definition qpagelayout.h:33
\inmodule QtGui
Definition qpagesize.h:22
bool isValid() const
Returns true if this page size is valid.
QString key() const
Returns the unique key of the page size.
QString name() const
Returns a localized human-readable name for the page size.
QSize sizePoints() const
Returns the size of the page in Postscript Points (1/72 of an inch).
static QPageSize createPageSize(const QString &key, const QSize &size, const QString &localizedName)
virtual QList< QPrint::ColorMode > supportedColorModes() const
QList< QPrint::InputSlot > m_inputSlots
virtual bool isRemote() const
QList< QPrint::ColorMode > m_colorModes
QList< QPageSize > m_pageSizes
virtual QPrint::InputSlot defaultInputSlot() const
QList< QPrint::OutputBin > m_outputBins
QList< QPrint::DuplexMode > m_duplexModes
virtual QPrint::OutputBin defaultOutputBin() const
\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
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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.
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
static QT_BEGIN_NAMESPACE QPrint::DuplexMode macToDuplexMode(const PMDuplexMode &mode)
const char * mimeType
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLenum mode
GLuint64 key
GLint GLsizei GLsizei height
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLint GLsizei width
#define Q_UNUSED(x)
QSettings settings("MySoft", "Star Runner")
[0]
QObject::connect nullptr
QMimeDatabase db
[0]
bool contains(const AT &t) const noexcept
Definition qlist.h:45