1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
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 <AppKit/AppKit.h>
41#include <ApplicationServices/ApplicationServices.h>
42
43#include "qprintengine_mac_p.h"
44#include "qcocoaprintersupport.h"
45#include <quuid.h>
46#include <QtGui/qpagelayout.h>
47#include <QtCore/qcoreapplication.h>
48#include <QtCore/qdebug.h>
49
50#include <QtCore/private/qcore_mac_p.h>
51
52#ifndef QT_NO_PRINTER
53
54QT_BEGIN_NAMESPACE
55
56extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits);
57
58QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode, const QString &deviceId)
59    : QPaintEngine(*(new QMacPrintEnginePrivate))
60{
61    Q_D(QMacPrintEngine);
62    d->mode = mode;
63    QString id = deviceId;
64    if (id.isEmpty())
65        id = QCocoaPrinterSupport().defaultPrintDeviceId();
66    else
67        setProperty(QPrintEngine::PPK_PrinterName, deviceId);
68    d->m_printDevice.reset(new QCocoaPrintDevice(id));
69    d->m_pageLayout.setPageSize(d->m_printDevice->defaultPageSize());
70    d->initialize();
71}
72
73bool QMacPrintEngine::begin(QPaintDevice *dev)
74{
75    Q_D(QMacPrintEngine);
76
77    Q_ASSERT(dev && dev->devType() == QInternal::Printer);
78    if (!static_cast<QPrinter *>(dev)->isValid())
79        return false;
80
81    if (d->state == QPrinter::Idle && !d->isPrintSessionInitialized()) // Need to reinitialize
82        d->initialize();
83
84    d->paintEngine->state = state;
85    d->paintEngine->begin(dev);
86    Q_ASSERT_X(d->state == QPrinter::Idle, "QMacPrintEngine", "printer already active");
87
88    if (PMSessionValidatePrintSettings(d->session(), d->settings(), kPMDontWantBoolean) != noErr
89        || PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean) != noErr) {
90        d->state = QPrinter::Error;
91        return false;
92    }
93
94    if (!d->outputFilename.isEmpty()) {
95        QCFType<CFURLRef> outFile = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault,
96                                                                  QCFString(d->outputFilename),
97                                                                  kCFURLPOSIXPathStyle,
98                                                                  false);
99        if (PMSessionSetDestination(d->session(), d->settings(), kPMDestinationFile,
100                                    kPMDocumentFormatPDF, outFile) != noErr) {
101            qWarning("QMacPrintEngine::begin: Problem setting file [%s]", d->outputFilename.toUtf8().constData());
102            return false;
103        }
104    }
105
106    OSStatus status = PMSessionBeginCGDocumentNoDialog(d->session(), d->settings(), d->format());
107    if (status != noErr) {
108        d->state = QPrinter::Error;
109        return false;
110    }
111
112    d->state = QPrinter::Active;
113    setActive(true);
114    d->newPage_helper();
115    return true;
116}
117
118bool QMacPrintEngine::end()
119{
120    Q_D(QMacPrintEngine);
121    if (d->state == QPrinter::Aborted)
122        return true;  // I was just here a function call ago :)
123    if (d->paintEngine->type() == QPaintEngine::CoreGraphics) {
124        // We don't need the paint engine to call restoreGraphicsState()
125        static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->stackCount = 0;
126        static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->hd = nullptr;
127    }
128    d->paintEngine->end();
129    if (d->state != QPrinter::Idle)
130        d->releaseSession();
131    d->state  = QPrinter::Idle;
132    return true;
133}
134
135QPaintEngine *
136QMacPrintEngine::paintEngine() const
137{
138    return d_func()->paintEngine;
139}
140
141Qt::HANDLE QMacPrintEngine::handle() const
142{
143    QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine());
144    return cgEngine->d_func()->hd;
145}
146
147QMacPrintEnginePrivate::~QMacPrintEnginePrivate()
148{
149    [printInfo release];
150    delete paintEngine;
151}
152
153QPrinter::PrinterState QMacPrintEngine::printerState() const
154{
155    return d_func()->state;
156}
157
158bool QMacPrintEngine::newPage()
159{
160    Q_D(QMacPrintEngine);
161    Q_ASSERT(d->state == QPrinter::Active);
162    OSStatus err = PMSessionEndPageNoDialog(d->session());
163    if (err != noErr)  {
164        if (err == kPMCancel) {
165            // User canceled, we need to abort!
166            abort();
167        } else {
168            // Not sure what the problem is...
169            qWarning("QMacPrintEngine::newPage: Cannot end current page. %ld", long(err));
170            d->state = QPrinter::Error;
171        }
172        return false;
173    }
174    return d->newPage_helper();
175}
176
177bool QMacPrintEngine::abort()
178{
179    Q_D(QMacPrintEngine);
180    if (d->state != QPrinter::Active)
181        return false;
182    bool ret = end();
183    d->state = QPrinter::Aborted;
184    return ret;
185}
186
187int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
188{
189    Q_D(const QMacPrintEngine);
190    int val = 1;
191    switch (m) {
192    case QPaintDevice::PdmWidth:
193        val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).width();
194        break;
195    case QPaintDevice::PdmHeight:
196        val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).height();
197        break;
198    case QPaintDevice::PdmWidthMM:
199        val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).width());
200        break;
201    case QPaintDevice::PdmHeightMM:
202        val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).height());
203        break;
204    case QPaintDevice::PdmPhysicalDpiX:
205    case QPaintDevice::PdmPhysicalDpiY: {
206        PMPrinter printer;
207        if (PMSessionGetCurrentPrinter(d->session(), &printer) == noErr) {
208            PMResolution resolution;
209            PMPrinterGetOutputResolution(printer, d->settings(), &resolution);
210            val = (int)resolution.vRes;
211            break;
212        }
213        Q_FALLTHROUGH();
214    }
215    case QPaintDevice::PdmDpiY:
216        val = (int)d->resolution.vRes;
217        break;
218    case QPaintDevice::PdmDpiX:
219        val = (int)d->resolution.hRes;
220        break;
221    case QPaintDevice::PdmNumColors:
222        val = (1 << metric(QPaintDevice::PdmDepth));
223        break;
224    case QPaintDevice::PdmDepth:
225        val = 24;
226        break;
227    case QPaintDevice::PdmDevicePixelRatio:
228        val = 1;
229        break;
230    case QPaintDevice::PdmDevicePixelRatioScaled:
231        val = 1 * QPaintDevice::devicePixelRatioFScale();
232        break;
233    default:
234        val = 0;
235        qWarning("QPrinter::metric: Invalid metric command");
236    }
237    return val;
238}
239
240void QMacPrintEnginePrivate::initialize()
241{
242    Q_Q(QMacPrintEngine);
243
244    Q_ASSERT(!printInfo);
245
246    if (!paintEngine)
247        paintEngine = new QCoreGraphicsPaintEngine();
248
249    q->gccaps = paintEngine->gccaps;
250
251    QMacAutoReleasePool pool;
252    printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]];
253
254    QList<int> resolutions = m_printDevice->supportedResolutions();
255    if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) {
256        std::sort(resolutions.begin(), resolutions.end());
257        if (resolutions.count() > 1 && mode == QPrinter::HighResolution)
258            resolution.hRes = resolution.vRes = resolutions.last();
259        else
260            resolution.hRes = resolution.vRes = resolutions.first();
261        if (resolution.hRes == 0)
262            resolution.hRes = resolution.vRes = 600;
263    } else {
264        resolution.hRes = resolution.vRes = qt_defaultDpi();
265    }
266
267    setPageSize(m_pageLayout.pageSize());
268
269    QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant>::const_iterator propC;
270    for (propC = valueCache.constBegin(); propC != valueCache.constEnd(); ++propC) {
271        q->setProperty(propC.key(), propC.value());
272    }
273}
274
275void QMacPrintEnginePrivate::releaseSession()
276{
277    PMSessionEndPageNoDialog(session());
278    PMSessionEndDocumentNoDialog(session());
279    [printInfo release];
280    printInfo = nil;
281}
282
283bool QMacPrintEnginePrivate::newPage_helper()
284{
285    Q_Q(QMacPrintEngine);
286    Q_ASSERT(state == QPrinter::Active);
287
288    if (PMSessionError(session()) != noErr) {
289        q->abort();
290        return false;
291    }
292
293    // pop the stack of saved graphic states, in case we get the same
294    // context back - either way, the stack count should be 0 when we
295    // get the new one
296    QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine);
297    while (cgEngine->d_func()->stackCount > 0)
298        cgEngine->d_func()->restoreGraphicsState();
299
300    OSStatus status = PMSessionBeginPageNoDialog(session(), format(), nullptr);
301    if (status != noErr) {
302        state = QPrinter::Error;
303        return false;
304    }
305
306    QRect page = m_pageLayout.paintRectPixels(resolution.hRes);
307    QRect paper = m_pageLayout.fullRectPixels(resolution.hRes);
308
309    CGContextRef cgContext;
310    OSStatus err = noErr;
311    err = PMSessionGetCGGraphicsContext(session(), &cgContext);
312    if (err != noErr) {
313        qWarning("QMacPrintEngine::newPage: Cannot retrieve CoreGraphics context: %ld", long(err));
314        state = QPrinter::Error;
315        return false;
316    }
317    cgEngine->d_func()->hd = cgContext;
318
319    // Set the resolution as a scaling ration of 72 (the default).
320    CGContextScaleCTM(cgContext, 72 / resolution.hRes, 72 / resolution.vRes);
321
322    CGContextScaleCTM(cgContext, 1, -1);
323    CGContextTranslateCTM(cgContext, 0, -paper.height());
324    if (m_pageLayout.mode() != QPageLayout::FullPageMode)
325        CGContextTranslateCTM(cgContext, page.x() - paper.x(), page.y() - paper.y());
326    cgEngine->d_func()->orig_xform = CGContextGetCTM(cgContext);
327    cgEngine->d_func()->setClip(nullptr);
328    cgEngine->state->dirtyFlags = QPaintEngine::DirtyFlag(QPaintEngine::AllDirty
329                                                          & ~(QPaintEngine::DirtyClipEnabled
330                                                              | QPaintEngine::DirtyClipRegion
331                                                              | QPaintEngine::DirtyClipPath));
332    if (cgEngine->painter()->hasClipping())
333        cgEngine->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled;
334    cgEngine->syncState();
335    return true;
336}
337
338void QMacPrintEnginePrivate::setPageSize(const QPageSize &pageSize)
339{
340    if (!pageSize.isValid())
341        return;
342
343    // Get the matching printer paper
344    QPageSize printerPageSize = m_printDevice->supportedPageSize(pageSize);
345    QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize;
346
347    // Get the PMPaper and check it is valid
348    PMPaper macPaper = m_printDevice->macPaper(usePageSize);
349    if (!macPaper) {
350        qWarning() << "QMacPrintEngine: Invalid PMPaper returned for " << pageSize;
351        return;
352    }
353
354    QMarginsF printable = m_printDevice->printableMargins(usePageSize, m_pageLayout.orientation(), resolution.hRes);
355    m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units()));
356
357    // You cannot set the page size on a PMPageFormat, you must create a new PMPageFormat
358    PMPageFormat pageFormat;
359    PMCreatePageFormatWithPMPaper(&pageFormat, macPaper);
360    PMSetOrientation(pageFormat, m_pageLayout.orientation() == QPageLayout::Landscape ? kPMLandscape : kPMPortrait, kPMUnlocked);
361    PMCopyPageFormat(pageFormat, format());
362    if (PMSessionValidatePageFormat(session(), format(), kPMDontWantBoolean) != noErr)
363        qWarning("QMacPrintEngine: Invalid page format");
364    PMRelease(pageFormat);
365}
366
367void QMacPrintEngine::updateState(const QPaintEngineState &state)
368{
369    d_func()->paintEngine->updateState(state);
370}
371
372void QMacPrintEngine::drawRects(const QRectF *r, int num)
373{
374    Q_D(QMacPrintEngine);
375    Q_ASSERT(d->state == QPrinter::Active);
376    d->paintEngine->drawRects(r, num);
377}
378
379void QMacPrintEngine::drawPoints(const QPointF *points, int pointCount)
380{
381    Q_D(QMacPrintEngine);
382    Q_ASSERT(d->state == QPrinter::Active);
383    d->paintEngine->drawPoints(points, pointCount);
384}
385
386void QMacPrintEngine::drawEllipse(const QRectF &r)
387{
388    Q_D(QMacPrintEngine);
389    Q_ASSERT(d->state == QPrinter::Active);
390    d->paintEngine->drawEllipse(r);
391}
392
393void QMacPrintEngine::drawLines(const QLineF *lines, int lineCount)
394{
395    Q_D(QMacPrintEngine);
396    Q_ASSERT(d->state == QPrinter::Active);
397    d->paintEngine->drawLines(lines, lineCount);
398}
399
400void QMacPrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
401{
402    Q_D(QMacPrintEngine);
403    Q_ASSERT(d->state == QPrinter::Active);
404    d->paintEngine->drawPolygon(points, pointCount, mode);
405}
406
407void QMacPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
408{
409    Q_D(QMacPrintEngine);
410    Q_ASSERT(d->state == QPrinter::Active);
411    d->paintEngine->drawPixmap(r, pm, sr);
412}
413
414void QMacPrintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags)
415{
416    Q_D(QMacPrintEngine);
417    Q_ASSERT(d->state == QPrinter::Active);
418    d->paintEngine->drawImage(r, pm, sr, flags);
419}
420
421void QMacPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
422{
423    Q_D(QMacPrintEngine);
424    Q_ASSERT(d->state == QPrinter::Active);
425    if (!d->embedFonts)
426        QPaintEngine::drawTextItem(p, ti);
427    else
428        d->paintEngine->drawTextItem(p, ti);
429}
430
431void QMacPrintEngine::drawTiledPixmap(const QRectF &dr, const QPixmap &pixmap, const QPointF &sr)
432{
433    Q_D(QMacPrintEngine);
434    Q_ASSERT(d->state == QPrinter::Active);
435    d->paintEngine->drawTiledPixmap(dr, pixmap, sr);
436}
437
438void QMacPrintEngine::drawPath(const QPainterPath &path)
439{
440    Q_D(QMacPrintEngine);
441    Q_ASSERT(d->state == QPrinter::Active);
442    d->paintEngine->drawPath(path);
443}
444
445
446void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
447{
448    Q_D(QMacPrintEngine);
449
450    d->valueCache.insert(key, value);
451    if (!d->printInfo)
452        return;
453
454    switch (key) {
455
456    // The following keys are properties or derived values and so cannot be set
457    case PPK_PageRect:
458        break;
459    case PPK_PaperRect:
460        break;
461    case PPK_PaperSources:
462        break;
463    case PPK_SupportsMultipleCopies:
464        break;
465    case PPK_SupportedResolutions:
466        break;
467
468    // The following keys are settings that are unsupported by the Mac PrintEngine
469    case PPK_ColorMode:
470        break;
471    case PPK_CustomBase:
472        break;
473    case PPK_PageOrder:
474        // TODO Check if can be supported via Cups Options
475        break;
476    case PPK_PaperSource:
477        // TODO Check if can be supported via Cups Options
478        break;
479    case PPK_PrinterProgram:
480        break;
481    case PPK_SelectionOption:
482        break;
483
484    // The following keys are properties and settings that are supported by the Mac PrintEngine
485    case PPK_FontEmbedding:
486        d->embedFonts = value.toBool();
487        break;
488    case PPK_Resolution:  {
489        int bestResolution = 0;
490        int dpi = value.toInt();
491        int bestDistance = INT_MAX;
492        for (int resolution : d->m_printDevice->supportedResolutions()) {
493            if (dpi == resolution) {
494                bestResolution = resolution;
495                break;
496            } else {
497                int distance = qAbs(dpi - resolution);
498                if (distance < bestDistance) {
499                    bestDistance = distance;
500                    bestResolution = resolution;
501                }
502            }
503        }
504        PMResolution resolution;
505        resolution.hRes = resolution.vRes = bestResolution;
506        if (PMPrinterSetOutputResolution(d->m_printDevice->macPrinter(), d->settings(), &resolution) == noErr) {
507            // Setting the resolution succeeded.
508            // Now try to read the actual resolution selected by the OS.
509            if (PMPrinterGetOutputResolution(d->m_printDevice->macPrinter(), d->settings(), &d->resolution) != noErr) {
510                // Reading the resolution somehow failed; d->resolution is in undefined state.
511                // So use the value which was acceptable to PMPrinterSetOutputResolution.
512                d->resolution = resolution;
513            }
514        }
515        break;
516    }
517    case PPK_CollateCopies:
518        PMSetCollate(d->settings(), value.toBool());
519        break;
520    case PPK_Creator:
521        d->m_creator = value.toString();
522        break;
523    case PPK_DocumentName:
524        PMPrintSettingsSetJobName(d->settings(), QCFString(value.toString()));
525        break;
526    case PPK_Duplex: {
527        QPrint::DuplexMode mode = QPrint::DuplexMode(value.toInt());
528        if (mode == property(PPK_Duplex).toInt() || !d->m_printDevice->supportedDuplexModes().contains(mode))
529            break;
530        switch (mode) {
531        case QPrint::DuplexNone:
532            PMSetDuplex(d->settings(), kPMDuplexNone);
533            break;
534        case QPrint::DuplexAuto:
535            PMSetDuplex(d->settings(), d->m_pageLayout.orientation() == QPageLayout::Landscape ? kPMDuplexTumble : kPMDuplexNoTumble);
536            break;
537        case QPrint::DuplexLongSide:
538            PMSetDuplex(d->settings(), kPMDuplexNoTumble);
539            break;
540        case QPrint::DuplexShortSide:
541            PMSetDuplex(d->settings(), kPMDuplexTumble);
542            break;
543        default:
544            // Don't change
545            break;
546        }
547        break;
548    }
549    case PPK_FullPage:
550        if (value.toBool())
551            d->m_pageLayout.setMode(QPageLayout::FullPageMode);
552        else
553            d->m_pageLayout.setMode(QPageLayout::StandardMode);
554        break;
555    case PPK_CopyCount: // fallthrough
556    case PPK_NumberOfCopies:
557        PMSetCopies(d->settings(), value.toInt(), false);
558        break;
559    case PPK_Orientation: {
560        // First try set the Mac format orientation, then set our orientation to match result
561        QPageLayout::Orientation newOrientation = QPageLayout::Orientation(value.toInt());
562        PMOrientation macOrientation = (newOrientation == QPageLayout::Landscape) ? kPMLandscape : kPMPortrait;
563        PMSetOrientation(d->format(), macOrientation, kPMUnlocked);
564        PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean);
565        PMGetOrientation(d->format(), &macOrientation);
566        d->m_pageLayout.setOrientation(macOrientation == kPMLandscape ? QPageLayout::Landscape : QPageLayout::Portrait);
567        break;
568    }
569    case PPK_OutputFileName:
570        d->outputFilename = value.toString();
571        break;
572    case PPK_PageSize:
573        d->setPageSize(QPageSize(QPageSize::PageSizeId(value.toInt())));
574        break;
575    case PPK_PaperName:
576        // Get the named page size from the printer if supported
577        d->setPageSize(d->m_printDevice->supportedPageSize(value.toString()));
578        break;
579    case PPK_WindowsPageSize:
580        d->setPageSize(QPageSize(QPageSize::id(value.toInt())));
581        break;
582    case PPK_PrinterName: {
583        QVariant pageSize = QVariant::fromValue(d->m_pageLayout.pageSize());
584        const bool isFullPage = d->m_pageLayout.mode() == QPageLayout::FullPageMode;
585        QVariant orientation = QVariant::fromValue(d->m_pageLayout.orientation());
586        QVariant margins = QVariant::fromValue(QPair<QMarginsF, QPageLayout::Unit>(d->m_pageLayout.margins(),
587                                                                                   d->m_pageLayout.units()));
588        QString id = value.toString();
589        if (id.isEmpty())
590            id = QCocoaPrinterSupport().defaultPrintDeviceId();
591        else if (!QCocoaPrinterSupport().availablePrintDeviceIds().contains(id))
592            break;
593        d->m_printDevice.reset(new QCocoaPrintDevice(id));
594        PMPrinter printer = d->m_printDevice->macPrinter();
595        PMRetain(printer);
596        PMSessionSetCurrentPMPrinter(d->session(), printer);
597        // Ensure the settings are up to date and valid
598        if (d->m_printDevice->supportedPageSize(pageSize.value<QPageSize>()).isValid())
599            setProperty(PPK_QPageSize, pageSize);
600        else
601            setProperty(PPK_CustomPaperSize, pageSize.value<QPageSize>().size(QPageSize::Point));
602        setProperty(PPK_FullPage, QVariant(isFullPage));
603        setProperty(PPK_Orientation, orientation);
604        setProperty(PPK_QPageMargins, margins);
605        break;
606    }
607    case PPK_CustomPaperSize:
608        d->setPageSize(QPageSize(value.toSizeF(), QPageSize::Point));
609        break;
610    case PPK_PageMargins:
611    {
612        QList<QVariant> margins(value.toList());
613        Q_ASSERT(margins.size() == 4);
614        d->m_pageLayout.setMargins(QMarginsF(margins.at(0).toReal(), margins.at(1).toReal(),
615                                             margins.at(2).toReal(), margins.at(3).toReal()));
616        break;
617    }
618    case PPK_QPageSize:
619        d->setPageSize(value.value<QPageSize>());
620        break;
621    case PPK_QPageMargins: {
622        QPair<QMarginsF, QPageLayout::Unit> pair = value.value<QPair<QMarginsF, QPageLayout::Unit> >();
623        d->m_pageLayout.setUnits(pair.second);
624        d->m_pageLayout.setMargins(pair.first);
625        break;
626    }
627    case PPK_QPageLayout: {
628        QPageLayout pageLayout = value.value<QPageLayout>();
629        if (pageLayout.isValid() && d->m_printDevice->isValidPageLayout(pageLayout, d->resolution.hRes)) {
630            setProperty(PPK_QPageSize, QVariant::fromValue(pageLayout.pageSize()));
631            setProperty(PPK_FullPage, pageLayout.mode() == QPageLayout::FullPageMode);
632            setProperty(PPK_Orientation, QVariant::fromValue(pageLayout.orientation()));
633            d->m_pageLayout.setUnits(pageLayout.units());
634            d->m_pageLayout.setMargins(pageLayout.margins());
635        }
636        break;
637    }
638    // No default so that compiler will complain if new keys added and not handled in this engine
639    }
640}
641
642QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const
643{
644    Q_D(const QMacPrintEngine);
645    QVariant ret;
646
647    if (!d->printInfo && d->valueCache.contains(key))
648        return *d->valueCache.find(key);
649
650    switch (key) {
651
652    // The following keys are settings that are unsupported by the Mac PrintEngine
653    // Return sensible default values to ensure consistent behavior across platforms
654    case PPK_ColorMode:
655        ret = QPrinter::Color;
656        break;
657    case PPK_CustomBase:
658        // Special case, leave null
659        break;
660    case PPK_PageOrder:
661        // TODO Check if can be supported via Cups Options
662        ret = QPrinter::FirstPageFirst;
663        break;
664    case PPK_PaperSource:
665        // TODO Check if can be supported via Cups Options
666        ret = QPrinter::Auto;
667        break;
668    case PPK_PaperSources: {
669        // TODO Check if can be supported via Cups Options
670        QList<QVariant> out;
671        out << int(QPrinter::Auto);
672        ret = out;
673        break;
674        }
675    case PPK_PrinterProgram:
676        ret = QString();
677        break;
678    case PPK_SelectionOption:
679        ret = QString();
680        break;
681
682    // The following keys are properties and settings that are supported by the Mac PrintEngine
683    case PPK_FontEmbedding:
684        ret = d->embedFonts;
685        break;
686    case PPK_CollateCopies: {
687        Boolean status;
688        PMGetCollate(d->settings(), &status);
689        ret = bool(status);
690        break;
691    }
692    case PPK_Creator:
693        ret = d->m_creator;
694        break;
695    case PPK_DocumentName: {
696        CFStringRef name;
697        PMPrintSettingsGetJobName(d->settings(), &name);
698        ret = QString::fromCFString(name);
699        break;
700    }
701    case PPK_Duplex: {
702        PMDuplexMode mode = kPMDuplexNone;
703        PMGetDuplex(d->settings(), &mode);
704        switch (mode) {
705        case kPMDuplexNoTumble:
706            ret = QPrinter::DuplexLongSide;
707            break;
708        case kPMDuplexTumble:
709            ret = QPrinter::DuplexShortSide;
710            break;
711        case kPMDuplexNone:
712        default:
713            ret = QPrinter::DuplexNone;
714            break;
715        }
716        break;
717    }
718    case PPK_FullPage:
719        ret = d->m_pageLayout.mode() == QPageLayout::FullPageMode;
720        break;
721    case PPK_NumberOfCopies:
722        ret = 1;
723        break;
724    case PPK_CopyCount: {
725        UInt32 copies = 1;
726        PMGetCopies(d->settings(), &copies);
727        ret = (uint) copies;
728        break;
729    }
730    case PPK_SupportsMultipleCopies:
731        ret = true;
732        break;
733    case PPK_Orientation:
734        ret = d->m_pageLayout.orientation();
735        break;
736    case PPK_OutputFileName:
737        ret = d->outputFilename;
738        break;
739    case PPK_PageRect:
740        // PageRect is returned in device pixels
741        ret = d->m_pageLayout.paintRectPixels(d->resolution.hRes);
742        break;
743    case PPK_PageSize:
744        ret = d->m_pageLayout.pageSize().id();
745        break;
746    case PPK_PaperName:
747        ret = d->m_pageLayout.pageSize().name();
748        break;
749    case PPK_WindowsPageSize:
750        ret = d->m_pageLayout.pageSize().windowsId();
751        break;
752    case PPK_PaperRect:
753        // PaperRect is returned in device pixels
754        ret = d->m_pageLayout.fullRectPixels(d->resolution.hRes);
755        break;
756    case PPK_PrinterName:
757        return d->m_printDevice->id();
758        break;
759    case PPK_Resolution: {
760        ret = d->resolution.hRes;
761        break;
762    }
763    case PPK_SupportedResolutions: {
764        QList<QVariant> list;
765        for (int resolution : d->m_printDevice->supportedResolutions())
766            list << resolution;
767        ret = list;
768        break;
769    }
770    case PPK_CustomPaperSize:
771        ret = d->m_pageLayout.fullRectPoints().size();
772        break;
773    case PPK_PageMargins: {
774        QList<QVariant> list;
775        QMarginsF margins = d->m_pageLayout.margins(QPageLayout::Point);
776        list << margins.left() << margins.top() << margins.right() << margins.bottom();
777        ret = list;
778        break;
779    }
780    case PPK_QPageSize:
781        ret.setValue(d->m_pageLayout.pageSize());
782        break;
783    case PPK_QPageMargins: {
784        QPair<QMarginsF, QPageLayout::Unit> pair = qMakePair(d->m_pageLayout.margins(), d->m_pageLayout.units());
785        ret.setValue(pair);
786        break;
787    }
788    case PPK_QPageLayout:
789        ret.setValue(d->m_pageLayout);
790    // No default so that compiler will complain if new keys added and not handled in this engine
791    }
792    return ret;
793}
794
795QT_END_NAMESPACE
796
797#endif // QT_NO_PRINTER
798