1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtGui module 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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://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 2.1 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file.  Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <private/qprintengine_mac_p.h>
43#include <qthread.h>
44#include <quuid.h>
45#include <QtCore/qcoreapplication.h>
46
47#ifndef QT_NO_PRINTER
48
49QT_BEGIN_NAMESPACE
50
51extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
52
53QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode) : QPaintEngine(*(new QMacPrintEnginePrivate))
54{
55    Q_D(QMacPrintEngine);
56    d->mode = mode;
57    d->initialize();
58}
59
60bool QMacPrintEngine::begin(QPaintDevice *dev)
61{
62    Q_D(QMacPrintEngine);
63
64    Q_ASSERT(dev && dev->devType() == QInternal::Printer);
65    if (!static_cast<QPrinter *>(dev)->isValid())
66	return false;
67
68    if (d->state == QPrinter::Idle && !d->isPrintSessionInitialized()) // Need to reinitialize
69        d->initialize();
70
71    d->paintEngine->state = state;
72    d->paintEngine->begin(dev);
73    Q_ASSERT_X(d->state == QPrinter::Idle, "QMacPrintEngine", "printer already active");
74
75    if (PMSessionValidatePrintSettings(d->session, d->settings, kPMDontWantBoolean) != noErr
76        || PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean) != noErr) {
77        d->state = QPrinter::Error;
78        return false;
79    }
80
81    if (!d->outputFilename.isEmpty()) {
82        QCFType<CFURLRef> outFile = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault,
83                                                                  QCFString(d->outputFilename),
84                                                                  kCFURLPOSIXPathStyle,
85                                                                  false);
86        if (PMSessionSetDestination(d->session, d->settings, kPMDestinationFile,
87                                    kPMDocumentFormatPDF, outFile) != noErr) {
88            qWarning("QMacPrintEngine::begin: Problem setting file [%s]", d->outputFilename.toUtf8().constData());
89            return false;
90        }
91    }
92    OSStatus status = noErr;
93#ifndef QT_MAC_USE_COCOA
94    status = d->shouldSuppressStatus() ? PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format)
95                                       : PMSessionBeginCGDocument(d->session, d->settings, d->format);
96#else
97    status = PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format);
98#endif
99
100    if (status != noErr) {
101        d->state = QPrinter::Error;
102        return false;
103    }
104
105    d->state = QPrinter::Active;
106    setActive(true);
107    d->newPage_helper();
108    return true;
109}
110
111bool QMacPrintEngine::end()
112{
113    Q_D(QMacPrintEngine);
114    if (d->state == QPrinter::Aborted)
115        return true;  // I was just here a function call ago :)
116    if(d->paintEngine->type() == QPaintEngine::CoreGraphics) {
117        // We dont need the paint engine to call restoreGraphicsState()
118        static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->stackCount = 0;
119        static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->hd = 0;
120    }
121    d->paintEngine->end();
122    if (d->state != QPrinter::Idle)
123	d->releaseSession();
124    d->state  = QPrinter::Idle;
125    return true;
126}
127
128QPaintEngine *
129QMacPrintEngine::paintEngine() const
130{
131    return d_func()->paintEngine;
132}
133
134Qt::HANDLE QMacPrintEngine::handle() const
135{
136    QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine());
137    return cgEngine->d_func()->hd;
138}
139
140QMacPrintEnginePrivate::~QMacPrintEnginePrivate()
141{
142#ifdef QT_MAC_USE_COCOA
143    [printInfo release];
144#endif
145    delete paintEngine;
146}
147
148void QMacPrintEnginePrivate::setPaperSize(QPrinter::PaperSize ps)
149{
150    Q_Q(QMacPrintEngine);
151    if (hasCustomPaperSize) {
152        PMRelease(customPaper);
153        customPaper = 0;
154    }
155    hasCustomPaperSize = (ps == QPrinter::Custom);
156    PMPrinter printer;
157
158    if (PMSessionGetCurrentPrinter(session, &printer) == noErr) {
159        if (ps != QPrinter::Custom) {
160            QSize newSize = qt_paperSizeToQSizeF(ps).toSize();
161            QCFType<CFArrayRef> formats;
162            if (PMSessionCreatePageFormatList(session, printer, &formats) == noErr) {
163                CFIndex total = CFArrayGetCount(formats);
164                PMPageFormat tmp;
165                PMRect paper;
166                for (CFIndex idx = 0; idx < total; ++idx) {
167                    tmp = static_cast<PMPageFormat>(const_cast<void *>(CFArrayGetValueAtIndex(formats, idx)));
168                    PMGetUnadjustedPaperRect(tmp, &paper);
169                    int wMM = int((paper.right - paper.left) / 72 * 25.4 + 0.5);
170                    int hMM = int((paper.bottom - paper.top) / 72 * 25.4 + 0.5);
171                    if (newSize.width() == wMM && newSize.height() == hMM) {
172                        PMCopyPageFormat(tmp, format);
173                        // reset the orientation and resolution as they are lost in the copy.
174                        q->setProperty(QPrintEngine::PPK_Orientation, orient);
175                        if (PMSessionValidatePageFormat(session, format, kPMDontWantBoolean) != noErr) {
176                            // Don't know, warn for the moment.
177                            qWarning("QMacPrintEngine, problem setting format and resolution for this page size");
178                        }
179                        break;
180                    }
181                }
182            }
183        } else {
184            QCFString paperId = QCFString::toCFStringRef(QUuid::createUuid().toString());
185            PMPaperMargins paperMargins;
186            paperMargins.left = leftMargin;
187            paperMargins.top = topMargin;
188            paperMargins.right = rightMargin;
189            paperMargins.bottom = bottomMargin;
190            PMPaperCreateCustom(printer, paperId, QCFString("Custom size"), customSize.width(), customSize.height(), &paperMargins, &customPaper);
191            PMPageFormat tmp;
192            PMCreatePageFormatWithPMPaper(&tmp, customPaper);
193            PMCopyPageFormat(tmp, format);
194            if (PMSessionValidatePageFormat(session, format, kPMDontWantBoolean) != noErr) {
195                // Don't know, warn for the moment.
196                qWarning("QMacPrintEngine, problem setting paper name");
197            }
198        }
199    }
200}
201
202QPrinter::PaperSize QMacPrintEnginePrivate::paperSize() const
203{
204    if (hasCustomPaperSize)
205        return QPrinter::Custom;
206    PMRect paper;
207    PMGetUnadjustedPaperRect(format, &paper);
208    int wMM = int((paper.right - paper.left) / 72 * 25.4 + 0.5);
209    int hMM = int((paper.bottom - paper.top) / 72 * 25.4 + 0.5);
210    for (int i = QPrinter::A4; i < QPrinter::NPaperSize; ++i) {
211        QSize s = qt_paperSizeToQSizeF(QPrinter::PaperSize(i)).toSize();
212        if (s.width() == wMM && s.height() == hMM)
213            return (QPrinter::PaperSize)i;
214    }
215    return QPrinter::Custom;
216}
217
218QList<QVariant> QMacPrintEnginePrivate::supportedResolutions() const
219{
220    Q_ASSERT_X(session, "QMacPrinterEngine::supportedResolutions",
221               "must have a valid printer session");
222    UInt32 resCount;
223    QList<QVariant> resolutions;
224    PMPrinter printer;
225    if (PMSessionGetCurrentPrinter(session, &printer) == noErr) {
226        PMResolution res;
227        OSStatus status = PMPrinterGetPrinterResolutionCount(printer, &resCount);
228        if (status  == kPMNotImplemented) {
229#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
230            // *Sigh* we have to use the non-indexed version.
231            if (PMPrinterGetPrinterResolution(printer, kPMMinSquareResolution, &res) == noErr)
232                resolutions.append(int(res.hRes));
233            if (PMPrinterGetPrinterResolution(printer, kPMMaxSquareResolution, &res) == noErr) {
234                QVariant var(int(res.hRes));
235                if (!resolutions.contains(var))
236                    resolutions.append(var);
237            }
238            if (PMPrinterGetPrinterResolution(printer, kPMDefaultResolution, &res) == noErr) {
239                QVariant var(int(res.hRes));
240                if (!resolutions.contains(var))
241                    resolutions.append(var);
242            }
243#endif
244        } else if (status == noErr) {
245            // According to the docs, index start at 1.
246            for (UInt32 i = 1; i <= resCount; ++i) {
247                if (PMPrinterGetIndexedPrinterResolution(printer, i, &res) == noErr)
248                    resolutions.append(QVariant(int(res.hRes)));
249            }
250        } else {
251            qWarning("QMacPrintEngine::supportedResolutions: Unexpected error: %ld", long(status));
252        }
253    }
254    return resolutions;
255}
256
257bool QMacPrintEnginePrivate::shouldSuppressStatus() const
258{
259    if (suppressStatus == true)
260        return true;
261
262    // Supress displaying the automatic progress dialog if we are printing
263    // from a non-gui thread.
264    return (qApp->thread() != QThread::currentThread());
265}
266
267QPrinter::PrinterState QMacPrintEngine::printerState() const
268{
269    return d_func()->state;
270}
271
272bool QMacPrintEngine::newPage()
273{
274    Q_D(QMacPrintEngine);
275    Q_ASSERT(d->state == QPrinter::Active);
276    OSStatus err =
277#ifndef QT_MAC_USE_COCOA
278    d->shouldSuppressStatus() ? PMSessionEndPageNoDialog(d->session)
279                              : PMSessionEndPage(d->session);
280#else
281    PMSessionEndPageNoDialog(d->session);
282#endif
283    if (err != noErr)  {
284        if (err == kPMCancel) {
285            // User canceled, we need to abort!
286            abort();
287        } else {
288            // Not sure what the problem is...
289            qWarning("QMacPrintEngine::newPage: Cannot end current page. %ld", long(err));
290            d->state = QPrinter::Error;
291        }
292        return false;
293    }
294    return d->newPage_helper();
295}
296
297bool QMacPrintEngine::abort()
298{
299    Q_D(QMacPrintEngine);
300    if (d->state != QPrinter::Active)
301        return false;
302    bool ret = end();
303    d->state = QPrinter::Aborted;
304    return ret;
305}
306
307static inline int qt_get_PDMWidth(PMPageFormat pformat, bool fullPage,
308                                  const PMResolution &resolution)
309{
310    int val = 0;
311    PMRect r;
312    qreal hRatio = resolution.hRes / 72;
313    if (fullPage) {
314        if (PMGetAdjustedPaperRect(pformat, &r) == noErr)
315            val = qRound((r.right - r.left) * hRatio);
316    } else {
317        if (PMGetAdjustedPageRect(pformat, &r) == noErr)
318            val = qRound((r.right - r.left) * hRatio);
319    }
320    return val;
321}
322
323static inline int qt_get_PDMHeight(PMPageFormat pformat, bool fullPage,
324                                   const PMResolution &resolution)
325{
326    int val = 0;
327    PMRect r;
328    qreal vRatio = resolution.vRes / 72;
329    if (fullPage) {
330        if (PMGetAdjustedPaperRect(pformat, &r) == noErr)
331            val = qRound((r.bottom - r.top) * vRatio);
332    } else {
333        if (PMGetAdjustedPageRect(pformat, &r) == noErr)
334            val = qRound((r.bottom - r.top) * vRatio);
335    }
336    return val;
337}
338
339
340int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
341{
342    Q_D(const QMacPrintEngine);
343    int val = 1;
344    switch (m) {
345    case QPaintDevice::PdmWidth:
346        if (d->hasCustomPaperSize) {
347            val = qRound(d->customSize.width());
348            if (d->hasCustomPageMargins) {
349                val -= qRound(d->leftMargin + d->rightMargin);
350            } else {
351                QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
352                val -= qRound(margins.at(0).toDouble() + margins.at(2).toDouble());
353            }
354        } else {
355            val = qt_get_PDMWidth(d->format, property(PPK_FullPage).toBool(), d->resolution);
356        }
357        break;
358    case QPaintDevice::PdmHeight:
359        if (d->hasCustomPaperSize) {
360            val = qRound(d->customSize.height());
361            if (d->hasCustomPageMargins) {
362                val -= qRound(d->topMargin + d->bottomMargin);
363            } else {
364                QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
365                val -= qRound(margins.at(1).toDouble() + margins.at(3).toDouble());
366            }
367        } else {
368            val = qt_get_PDMHeight(d->format, property(PPK_FullPage).toBool(), d->resolution);
369        }
370        break;
371    case QPaintDevice::PdmWidthMM:
372        val = metric(QPaintDevice::PdmWidth);
373        val = int((val * 254 + 5 * d->resolution.hRes) / (10 * d->resolution.hRes));
374        break;
375    case QPaintDevice::PdmHeightMM:
376        val = metric(QPaintDevice::PdmHeight);
377        val = int((val * 254 + 5 * d->resolution.vRes) / (10 * d->resolution.vRes));
378        break;
379    case QPaintDevice::PdmPhysicalDpiX:
380    case QPaintDevice::PdmPhysicalDpiY: {
381        PMPrinter printer;
382        if(PMSessionGetCurrentPrinter(d->session, &printer) == noErr) {
383            PMResolution resolution;
384#ifndef QT_MAC_USE_COCOA
385#  if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
386            if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
387                PMPrinterGetOutputResolution(printer, d->settings, &resolution);
388            } else
389#  endif
390            {
391                PMPrinterGetPrinterResolution(printer, kPMCurrentValue, &resolution);
392            }
393#else
394            PMPrinterGetOutputResolution(printer, d->settings, &resolution);
395#endif
396            val = (int)resolution.vRes;
397            break;
398        }
399        //otherwise fall through
400    }
401    case QPaintDevice::PdmDpiY:
402        val = (int)d->resolution.vRes;
403        break;
404    case QPaintDevice::PdmDpiX:
405        val = (int)d->resolution.hRes;
406        break;
407    case QPaintDevice::PdmNumColors:
408        val = (1 << metric(QPaintDevice::PdmDepth));
409        break;
410    case QPaintDevice::PdmDepth:
411        val = 24;
412        break;
413    default:
414        val = 0;
415        qWarning("QPrinter::metric: Invalid metric command");
416    }
417    return val;
418}
419
420void QMacPrintEnginePrivate::initialize()
421{
422    Q_Q(QMacPrintEngine);
423
424#ifndef QT_MAC_USE_COCOA
425    Q_ASSERT(!session);
426#else
427    Q_ASSERT(!printInfo);
428#endif
429
430    if (!paintEngine)
431        paintEngine = new QCoreGraphicsPaintEngine();
432
433    q->gccaps = paintEngine->gccaps;
434
435    fullPage = false;
436
437#ifndef QT_MAC_USE_COCOA
438    if (PMCreateSession(&session) != 0)
439        session = 0;
440#else
441    QMacCocoaAutoReleasePool pool;
442    printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]];
443    session = static_cast<PMPrintSession>([printInfo PMPrintSession]);
444#endif
445
446    PMPrinter printer;
447    if (session && PMSessionGetCurrentPrinter(session, &printer) == noErr) {
448        QList<QVariant> resolutions = supportedResolutions();
449        if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) {
450            if (resolutions.count() > 1 && mode == QPrinter::HighResolution) {
451                int max = 0;
452                for (int i = 0; i < resolutions.count(); ++i) {
453                    int value = resolutions.at(i).toInt();
454                    if (value > max)
455                        max = value;
456                }
457                resolution.hRes = resolution.vRes = max;
458            } else {
459                resolution.hRes = resolution.vRes = resolutions.at(0).toInt();
460            }
461            if(resolution.hRes == 0)
462                resolution.hRes = resolution.vRes = 600;
463        } else {
464            resolution.hRes = resolution.vRes = qt_defaultDpi();
465        }
466    }
467
468#ifndef QT_MAC_USE_COCOA
469    bool settingsInitialized = (settings != 0);
470    bool settingsOK = !settingsInitialized ? PMCreatePrintSettings(&settings) == noErr : true;
471    if (settingsOK && !settingsInitialized)
472        settingsOK = PMSessionDefaultPrintSettings(session, settings) == noErr;
473
474
475    bool formatInitialized = (format != 0);
476    bool formatOK = !formatInitialized ? PMCreatePageFormat(&format) == noErr : true;
477    if (formatOK) {
478        if (!formatInitialized) {
479            formatOK = PMSessionDefaultPageFormat(session, format) == noErr;
480        }
481        formatOK = PMSessionValidatePageFormat(session, format, kPMDontWantBoolean) == noErr;
482    }
483#else
484    settings = static_cast<PMPrintSettings>([printInfo PMPrintSettings]);
485    format = static_cast<PMPageFormat>([printInfo PMPageFormat]);
486#endif
487
488#ifndef QT_MAC_USE_COCOA
489    if (!settingsOK || !formatOK) {
490        qWarning("QMacPrintEngine::initialize: Unable to initialize QPainter");
491        state = QPrinter::Error;
492    }
493#endif
494
495    QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant>::const_iterator propC;
496    for (propC = valueCache.constBegin(); propC != valueCache.constEnd(); propC++) {
497        q->setProperty(propC.key(), propC.value());
498    }
499}
500
501void QMacPrintEnginePrivate::releaseSession()
502{
503#ifndef QT_MAC_USE_COCOA
504    if (shouldSuppressStatus()) {
505	PMSessionEndPageNoDialog(session);
506	PMSessionEndDocumentNoDialog(session);
507    } else {
508	PMSessionEndPage(session);
509	PMSessionEndDocument(session);
510    }
511    PMRelease(session);
512#else
513    PMSessionEndPageNoDialog(session);
514    PMSessionEndDocumentNoDialog(session);
515    [printInfo release];
516#endif
517    if (hasCustomPaperSize)
518        PMRelease(customPaper);
519    printInfo = 0;
520    session = 0;
521}
522
523bool QMacPrintEnginePrivate::newPage_helper()
524{
525    Q_Q(QMacPrintEngine);
526    Q_ASSERT(state == QPrinter::Active);
527
528    if (PMSessionError(session) != noErr) {
529        q->abort();
530        return false;
531    }
532
533    // pop the stack of saved graphic states, in case we get the same
534    // context back - either way, the stack count should be 0 when we
535    // get the new one
536    QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine);
537    while (cgEngine->d_func()->stackCount > 0)
538        cgEngine->d_func()->restoreGraphicsState();
539
540    OSStatus status =
541#ifndef QT_MAC_USE_COCOA
542        shouldSuppressStatus() ? PMSessionBeginPageNoDialog(session, format, 0)
543                               : PMSessionBeginPage(session, format, 0);
544#else
545        PMSessionBeginPageNoDialog(session, format, 0);
546#endif
547    if(status != noErr) {
548        state = QPrinter::Error;
549        return false;
550    }
551
552    QRect page = q->property(QPrintEngine::PPK_PageRect).toRect();
553    QRect paper = q->property(QPrintEngine::PPK_PaperRect).toRect();
554
555    CGContextRef cgContext;
556    OSStatus err = noErr;
557    err = PMSessionGetCGGraphicsContext(session, &cgContext);
558    if(err != noErr) {
559        qWarning("QMacPrintEngine::newPage: Cannot retrieve CoreGraphics context: %ld", long(err));
560        state = QPrinter::Error;
561        return false;
562    }
563    cgEngine->d_func()->hd = cgContext;
564
565    // Set the resolution as a scaling ration of 72 (the default).
566    CGContextScaleCTM(cgContext, 72 / resolution.hRes, 72 / resolution.vRes);
567
568    CGContextScaleCTM(cgContext, 1, -1);
569    CGContextTranslateCTM(cgContext, 0, -paper.height());
570    if (!fullPage)
571        CGContextTranslateCTM(cgContext, page.x() - paper.x(), page.y() - paper.y());
572    cgEngine->d_func()->orig_xform = CGContextGetCTM(cgContext);
573    cgEngine->d_func()->setClip(0);
574    cgEngine->state->dirtyFlags = QPaintEngine::DirtyFlag(QPaintEngine::AllDirty
575							  & ~(QPaintEngine::DirtyClipEnabled
576							      | QPaintEngine::DirtyClipRegion
577							      | QPaintEngine::DirtyClipPath));
578    if (cgEngine->painter()->hasClipping())
579        cgEngine->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled;
580    cgEngine->syncState();
581    return true;
582}
583
584
585void QMacPrintEngine::updateState(const QPaintEngineState &state)
586{
587    d_func()->paintEngine->updateState(state);
588}
589
590void QMacPrintEngine::drawRects(const QRectF *r, int num)
591{
592    Q_D(QMacPrintEngine);
593    Q_ASSERT(d->state == QPrinter::Active);
594    d->paintEngine->drawRects(r, num);
595}
596
597void QMacPrintEngine::drawPoints(const QPointF *points, int pointCount)
598{
599    Q_D(QMacPrintEngine);
600    Q_ASSERT(d->state == QPrinter::Active);
601    d->paintEngine->drawPoints(points, pointCount);
602}
603
604void QMacPrintEngine::drawEllipse(const QRectF &r)
605{
606    Q_D(QMacPrintEngine);
607    Q_ASSERT(d->state == QPrinter::Active);
608    d->paintEngine->drawEllipse(r);
609}
610
611void QMacPrintEngine::drawLines(const QLineF *lines, int lineCount)
612{
613    Q_D(QMacPrintEngine);
614    Q_ASSERT(d->state == QPrinter::Active);
615    d->paintEngine->drawLines(lines, lineCount);
616}
617
618void QMacPrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
619{
620    Q_D(QMacPrintEngine);
621    Q_ASSERT(d->state == QPrinter::Active);
622    d->paintEngine->drawPolygon(points, pointCount, mode);
623}
624
625void QMacPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
626{
627    Q_D(QMacPrintEngine);
628    Q_ASSERT(d->state == QPrinter::Active);
629    d->paintEngine->drawPixmap(r, pm, sr);
630}
631
632void QMacPrintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags)
633{
634    Q_D(QMacPrintEngine);
635    Q_ASSERT(d->state == QPrinter::Active);
636    d->paintEngine->drawImage(r, pm, sr, flags);
637}
638
639void QMacPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
640{
641    Q_D(QMacPrintEngine);
642    Q_ASSERT(d->state == QPrinter::Active);
643    d->paintEngine->drawTextItem(p, ti);
644}
645
646void QMacPrintEngine::drawTiledPixmap(const QRectF &dr, const QPixmap &pixmap, const QPointF &sr)
647{
648    Q_D(QMacPrintEngine);
649    Q_ASSERT(d->state == QPrinter::Active);
650    d->paintEngine->drawTiledPixmap(dr, pixmap, sr);
651}
652
653void QMacPrintEngine::drawPath(const QPainterPath &path)
654{
655    Q_D(QMacPrintEngine);
656    Q_ASSERT(d->state == QPrinter::Active);
657    d->paintEngine->drawPath(path);
658}
659
660
661void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
662{
663    Q_D(QMacPrintEngine);
664
665    d->valueCache.insert(key, value);
666    if (!d->session)
667        return;
668
669    switch (key) {
670    case PPK_CollateCopies:
671        break;
672    case PPK_ColorMode:
673        break;
674    case PPK_Creator:
675        break;
676    case PPK_DocumentName:
677        break;
678    case PPK_PageOrder:
679        break;
680    case PPK_PaperSource:
681        break;
682    case PPK_SelectionOption:
683        break;
684    case PPK_Resolution:  {
685        PMPrinter printer;
686        UInt32 count;
687        if (PMSessionGetCurrentPrinter(d->session, &printer) != noErr)
688            break;
689        if (PMPrinterGetPrinterResolutionCount(printer, &count) != noErr)
690            break;
691        PMResolution resolution = { 0.0, 0.0 };
692        PMResolution bestResolution = { 0.0, 0.0 };
693        int dpi = value.toInt();
694        int bestDistance = INT_MAX;
695        for (UInt32 i = 1; i <= count; ++i) {  // Yes, it starts at 1
696            if (PMPrinterGetIndexedPrinterResolution(printer, i, &resolution) == noErr) {
697                if (dpi == int(resolution.hRes)) {
698                    bestResolution = resolution;
699                    break;
700                } else {
701                    int distance = qAbs(dpi - int(resolution.hRes));
702                    if (distance < bestDistance) {
703                        bestDistance = distance;
704                        bestResolution = resolution;
705                    }
706                }
707            }
708        }
709        PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean);
710        break;
711    }
712
713    case PPK_FullPage:
714        d->fullPage = value.toBool();
715        break;
716    case PPK_CopyCount: // fallthrough
717    case PPK_NumberOfCopies:
718        PMSetCopies(d->settings, value.toInt(), false);
719        break;
720    case PPK_Orientation: {
721        if (d->state == QPrinter::Active) {
722            qWarning("QMacPrintEngine::setOrientation: Orientation cannot be changed during a print job, ignoring change");
723        } else {
724            QPrinter::Orientation newOrientation = QPrinter::Orientation(value.toInt());
725            if (d->hasCustomPaperSize && (d->orient != newOrientation))
726                d->customSize = QSizeF(d->customSize.height(), d->customSize.width());
727            d->orient = newOrientation;
728            PMOrientation o = d->orient == QPrinter::Portrait ? kPMPortrait : kPMLandscape;
729            PMSetOrientation(d->format, o, false);
730            PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean);
731        }
732        break; }
733    case PPK_OutputFileName:
734        d->outputFilename = value.toString();
735        break;
736    case PPK_PaperSize:
737        d->setPaperSize(QPrinter::PaperSize(value.toInt()));
738        break;
739    case PPK_PrinterName: {
740        bool printerNameSet = false;
741        OSStatus status = noErr;
742        QCFType<CFArrayRef> printerList;
743        status = PMServerCreatePrinterList(kPMServerLocal, &printerList);
744        if (status == noErr) {
745            CFIndex count = CFArrayGetCount(printerList);
746            for (CFIndex i=0; i<count; ++i) {
747                PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i)));
748                QString name = QCFString::toQString(PMPrinterGetName(printer));
749                if (name == value.toString()) {
750                    status = PMSessionSetCurrentPMPrinter(d->session, printer);
751                    printerNameSet = true;
752                    break;
753                }
754            }
755        }
756        if (status != noErr)
757            qWarning("QMacPrintEngine::setPrinterName: Error setting printer: %ld", long(status));
758	if (!printerNameSet) {
759            qWarning("QMacPrintEngine::setPrinterName: Failed to set printer named '%s'.", qPrintable(value.toString()));
760            d->releaseSession();
761            d->state = QPrinter::Idle;
762        }
763        break; }
764    case PPK_SuppressSystemPrintStatus:
765        d->suppressStatus = value.toBool();
766        break;
767    case PPK_CustomPaperSize:
768    {
769        PMOrientation orientation;
770        PMGetOrientation(d->format, &orientation);
771        d->customSize = value.toSizeF();
772        if (orientation != kPMPortrait)
773            d->customSize = QSizeF(d->customSize.height(), d->customSize.width());
774        d->setPaperSize(QPrinter::Custom);
775        break;
776    }
777    case PPK_PageMargins:
778    {
779        QList<QVariant> margins(value.toList());
780        Q_ASSERT(margins.size() == 4);
781        d->leftMargin = margins.at(0).toDouble();
782        d->topMargin = margins.at(1).toDouble();
783        d->rightMargin = margins.at(2).toDouble();
784        d->bottomMargin = margins.at(3).toDouble();
785        d->hasCustomPageMargins = true;
786        break;
787    }
788
789    default:
790        break;
791    }
792}
793
794QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const
795{
796    Q_D(const QMacPrintEngine);
797    QVariant ret;
798
799    if (!d->session && d->valueCache.contains(key))
800        return *d->valueCache.find(key);
801
802    switch (key) {
803    case PPK_CollateCopies:
804        ret = false;
805        break;
806    case PPK_ColorMode:
807        ret = QPrinter::Color;
808        break;
809    case PPK_Creator:
810        break;
811    case PPK_DocumentName:
812        break;
813    case PPK_FullPage:
814        ret = d->fullPage;
815        break;
816    case PPK_NumberOfCopies:
817        ret = 1;
818        break;
819    case PPK_CopyCount: {
820        UInt32 copies = 1;
821        PMGetCopies(d->settings, &copies);
822        ret = (uint) copies;
823        break;
824    }
825    case PPK_SupportsMultipleCopies:
826        ret = true;
827        break;
828    case PPK_Orientation:
829        PMOrientation orientation;
830        PMGetOrientation(d->format, &orientation);
831        ret = orientation == kPMPortrait ? QPrinter::Portrait : QPrinter::Landscape;
832        break;
833    case PPK_OutputFileName:
834        ret = d->outputFilename;
835        break;
836    case PPK_PageOrder:
837        break;
838    case PPK_PaperSource:
839        break;
840    case PPK_PageRect: {
841        // PageRect is returned in device pixels
842        QRect r;
843        PMRect macrect, macpaper;
844        qreal hRatio = d->resolution.hRes / 72;
845        qreal vRatio = d->resolution.vRes / 72;
846        if (d->hasCustomPaperSize) {
847            r = QRect(0, 0, qRound(d->customSize.width() * hRatio), qRound(d->customSize.height() * vRatio));
848            if (d->hasCustomPageMargins) {
849                r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio),
850                         -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio));
851            } else {
852                QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
853                r.adjust(qRound(margins.at(0).toDouble() * hRatio),
854                         qRound(margins.at(1).toDouble() * vRatio),
855                         -qRound(margins.at(2).toDouble() * hRatio),
856                         -qRound(margins.at(3).toDouble()) * vRatio);
857            }
858        } else if (PMGetAdjustedPageRect(d->format, &macrect) == noErr
859                   && PMGetAdjustedPaperRect(d->format, &macpaper) == noErr)
860        {
861            if (d->fullPage || d->hasCustomPageMargins) {
862                r.setCoords(int(macpaper.left * hRatio), int(macpaper.top * vRatio),
863                            int(macpaper.right * hRatio), int(macpaper.bottom * vRatio));
864                r.translate(-r.x(), -r.y());
865                if (d->hasCustomPageMargins) {
866                    r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio),
867                             -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio));
868                }
869            } else {
870                r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio),
871                            int(macrect.right * hRatio), int(macrect.bottom * vRatio));
872                r.translate(int(-macpaper.left * hRatio), int(-macpaper.top * vRatio));
873            }
874        }
875        ret = r;
876        break; }
877    case PPK_PaperSize:
878        ret = d->paperSize();
879        break;
880    case PPK_PaperRect: {
881        QRect r;
882        PMRect macrect;
883        qreal hRatio = d->resolution.hRes / 72;
884        qreal vRatio = d->resolution.vRes / 72;
885        if (d->hasCustomPaperSize) {
886            r = QRect(0, 0, qRound(d->customSize.width() * hRatio), qRound(d->customSize.height() * vRatio));
887        } else if (PMGetAdjustedPaperRect(d->format, &macrect) == noErr) {
888            r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio),
889                        int(macrect.right * hRatio), int(macrect.bottom * vRatio));
890            r.translate(-r.x(), -r.y());
891        }
892        ret = r;
893        break; }
894    case PPK_PrinterName: {
895        PMPrinter printer;
896        OSStatus status = PMSessionGetCurrentPrinter(d->session, &printer);
897        if (status != noErr)
898            qWarning("QMacPrintEngine::printerName: Failed getting current PMPrinter: %ld", long(status));
899        if (printer)
900            ret = QCFString::toQString(PMPrinterGetName(printer));
901        break; }
902    case PPK_Resolution: {
903        ret = d->resolution.hRes;
904        break;
905    }
906    case PPK_SupportedResolutions:
907        ret = d->supportedResolutions();
908        break;
909    case PPK_CustomPaperSize:
910        ret = d->customSize;
911        break;
912    case PPK_PageMargins:
913    {
914        QList<QVariant> margins;
915        if (d->hasCustomPageMargins) {
916            margins << d->leftMargin << d->topMargin
917                    << d->rightMargin << d->bottomMargin;
918        } else {
919            PMPaperMargins paperMargins;
920            PMPaper paper;
921            PMGetPageFormatPaper(d->format, &paper);
922            PMPaperGetMargins(paper, &paperMargins);
923            margins << paperMargins.left << paperMargins.top
924                    << paperMargins.right << paperMargins.bottom;
925        }
926        ret = margins;
927        break;
928    }
929    default:
930        break;
931    }
932    return ret;
933}
934
935QT_END_NAMESPACE
936
937#endif // QT_NO_PRINTER
938