/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <AppKit/AppKit.h>
#include <ApplicationServices/ApplicationServices.h>

#include "qprintengine_mac_p.h"
#include "qcocoaprintersupport.h"
#include <quuid.h>
#include <QtGui/qpagelayout.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>

#include <QtCore/private/qcore_mac_p.h>

#ifndef QT_NO_PRINTER

QT_BEGIN_NAMESPACE

extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits);

QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode, const QString &deviceId)
    : QPaintEngine(*(new QMacPrintEnginePrivate))
{
    Q_D(QMacPrintEngine);
    d->mode = mode;
    QString id = deviceId;
    if (id.isEmpty())
        id = QCocoaPrinterSupport().defaultPrintDeviceId();
    else
        setProperty(QPrintEngine::PPK_PrinterName, deviceId);
    d->m_printDevice.reset(new QCocoaPrintDevice(id));
    d->m_pageLayout.setPageSize(d->m_printDevice->defaultPageSize());
    d->initialize();
}

bool QMacPrintEngine::begin(QPaintDevice *dev)
{
    Q_D(QMacPrintEngine);

    Q_ASSERT(dev && dev->devType() == QInternal::Printer);
    if (!static_cast<QPrinter *>(dev)->isValid())
        return false;

    if (d->state == QPrinter::Idle && !d->isPrintSessionInitialized()) // Need to reinitialize
        d->initialize();

    d->paintEngine->state = state;
    d->paintEngine->begin(dev);
    Q_ASSERT_X(d->state == QPrinter::Idle, "QMacPrintEngine", "printer already active");

    if (PMSessionValidatePrintSettings(d->session(), d->settings(), kPMDontWantBoolean) != noErr
        || PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean) != noErr) {
        d->state = QPrinter::Error;
        return false;
    }

    if (!d->outputFilename.isEmpty()) {
        QCFType<CFURLRef> outFile = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault,
                                                                  QCFString(d->outputFilename),
                                                                  kCFURLPOSIXPathStyle,
                                                                  false);
        if (PMSessionSetDestination(d->session(), d->settings(), kPMDestinationFile,
                                    kPMDocumentFormatPDF, outFile) != noErr) {
            qWarning("QMacPrintEngine::begin: Problem setting file [%s]", d->outputFilename.toUtf8().constData());
            return false;
        }
    }

    OSStatus status = PMSessionBeginCGDocumentNoDialog(d->session(), d->settings(), d->format());
    if (status != noErr) {
        d->state = QPrinter::Error;
        return false;
    }

    d->state = QPrinter::Active;
    setActive(true);
    d->newPage_helper();
    return true;
}

bool QMacPrintEngine::end()
{
    Q_D(QMacPrintEngine);
    if (d->state == QPrinter::Aborted)
        return true;  // I was just here a function call ago :)
    if (d->paintEngine->type() == QPaintEngine::CoreGraphics) {
        // We don't need the paint engine to call restoreGraphicsState()
        static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->stackCount = 0;
        static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->hd = nullptr;
    }
    d->paintEngine->end();
    if (d->state != QPrinter::Idle)
        d->releaseSession();
    d->state  = QPrinter::Idle;
    return true;
}

QPaintEngine *
QMacPrintEngine::paintEngine() const
{
    return d_func()->paintEngine;
}

Qt::HANDLE QMacPrintEngine::handle() const
{
    QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine());
    return cgEngine->d_func()->hd;
}

QMacPrintEnginePrivate::~QMacPrintEnginePrivate()
{
    [printInfo release];
    delete paintEngine;
}

QPrinter::PrinterState QMacPrintEngine::printerState() const
{
    return d_func()->state;
}

bool QMacPrintEngine::newPage()
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    OSStatus err = PMSessionEndPageNoDialog(d->session());
    if (err != noErr)  {
        if (err == kPMCancel) {
            // User canceled, we need to abort!
            abort();
        } else {
            // Not sure what the problem is...
            qWarning("QMacPrintEngine::newPage: Cannot end current page. %ld", long(err));
            d->state = QPrinter::Error;
        }
        return false;
    }
    return d->newPage_helper();
}

bool QMacPrintEngine::abort()
{
    Q_D(QMacPrintEngine);
    if (d->state != QPrinter::Active)
        return false;
    bool ret = end();
    d->state = QPrinter::Aborted;
    return ret;
}

int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
{
    Q_D(const QMacPrintEngine);
    int val = 1;
    switch (m) {
    case QPaintDevice::PdmWidth:
        val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).width();
        break;
    case QPaintDevice::PdmHeight:
        val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).height();
        break;
    case QPaintDevice::PdmWidthMM:
        val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).width());
        break;
    case QPaintDevice::PdmHeightMM:
        val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).height());
        break;
    case QPaintDevice::PdmPhysicalDpiX:
    case QPaintDevice::PdmPhysicalDpiY: {
        PMPrinter printer;
        if (PMSessionGetCurrentPrinter(d->session(), &printer) == noErr) {
            PMResolution resolution;
            PMPrinterGetOutputResolution(printer, d->settings(), &resolution);
            val = (int)resolution.vRes;
            break;
        }
        Q_FALLTHROUGH();
    }
    case QPaintDevice::PdmDpiY:
        val = (int)d->resolution.vRes;
        break;
    case QPaintDevice::PdmDpiX:
        val = (int)d->resolution.hRes;
        break;
    case QPaintDevice::PdmNumColors:
        val = (1 << metric(QPaintDevice::PdmDepth));
        break;
    case QPaintDevice::PdmDepth:
        val = 24;
        break;
    case QPaintDevice::PdmDevicePixelRatio:
        val = 1;
        break;
    case QPaintDevice::PdmDevicePixelRatioScaled:
        val = 1 * QPaintDevice::devicePixelRatioFScale();
        break;
    default:
        val = 0;
        qWarning("QPrinter::metric: Invalid metric command");
    }
    return val;
}

void QMacPrintEnginePrivate::initialize()
{
    Q_Q(QMacPrintEngine);

    Q_ASSERT(!printInfo);

    if (!paintEngine)
        paintEngine = new QCoreGraphicsPaintEngine();

    q->gccaps = paintEngine->gccaps;

    QMacAutoReleasePool pool;
    printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]];

    QList<int> resolutions = m_printDevice->supportedResolutions();
    if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) {
        std::sort(resolutions.begin(), resolutions.end());
        if (resolutions.count() > 1 && mode == QPrinter::HighResolution)
            resolution.hRes = resolution.vRes = resolutions.last();
        else
            resolution.hRes = resolution.vRes = resolutions.first();
        if (resolution.hRes == 0)
            resolution.hRes = resolution.vRes = 600;
    } else {
        resolution.hRes = resolution.vRes = qt_defaultDpi();
    }

    setPageSize(m_pageLayout.pageSize());

    QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant>::const_iterator propC;
    for (propC = valueCache.constBegin(); propC != valueCache.constEnd(); ++propC) {
        q->setProperty(propC.key(), propC.value());
    }
}

void QMacPrintEnginePrivate::releaseSession()
{
    PMSessionEndPageNoDialog(session());
    PMSessionEndDocumentNoDialog(session());
    [printInfo release];
    printInfo = nil;
}

bool QMacPrintEnginePrivate::newPage_helper()
{
    Q_Q(QMacPrintEngine);
    Q_ASSERT(state == QPrinter::Active);

    if (PMSessionError(session()) != noErr) {
        q->abort();
        return false;
    }

    // pop the stack of saved graphic states, in case we get the same
    // context back - either way, the stack count should be 0 when we
    // get the new one
    QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine);
    while (cgEngine->d_func()->stackCount > 0)
        cgEngine->d_func()->restoreGraphicsState();

    OSStatus status = PMSessionBeginPageNoDialog(session(), format(), nullptr);
    if (status != noErr) {
        state = QPrinter::Error;
        return false;
    }

    QRect page = m_pageLayout.paintRectPixels(resolution.hRes);
    QRect paper = m_pageLayout.fullRectPixels(resolution.hRes);

    CGContextRef cgContext;
    OSStatus err = noErr;
    err = PMSessionGetCGGraphicsContext(session(), &cgContext);
    if (err != noErr) {
        qWarning("QMacPrintEngine::newPage: Cannot retrieve CoreGraphics context: %ld", long(err));
        state = QPrinter::Error;
        return false;
    }
    cgEngine->d_func()->hd = cgContext;

    // Set the resolution as a scaling ration of 72 (the default).
    CGContextScaleCTM(cgContext, 72 / resolution.hRes, 72 / resolution.vRes);

    CGContextScaleCTM(cgContext, 1, -1);
    CGContextTranslateCTM(cgContext, 0, -paper.height());
    if (m_pageLayout.mode() != QPageLayout::FullPageMode)
        CGContextTranslateCTM(cgContext, page.x() - paper.x(), page.y() - paper.y());
    cgEngine->d_func()->orig_xform = CGContextGetCTM(cgContext);
    cgEngine->d_func()->setClip(nullptr);
    cgEngine->state->dirtyFlags = QPaintEngine::DirtyFlag(QPaintEngine::AllDirty
                                                          & ~(QPaintEngine::DirtyClipEnabled
                                                              | QPaintEngine::DirtyClipRegion
                                                              | QPaintEngine::DirtyClipPath));
    if (cgEngine->painter()->hasClipping())
        cgEngine->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled;
    cgEngine->syncState();
    return true;
}

void QMacPrintEnginePrivate::setPageSize(const QPageSize &pageSize)
{
    if (!pageSize.isValid())
        return;

    // Get the matching printer paper
    QPageSize printerPageSize = m_printDevice->supportedPageSize(pageSize);
    QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize;

    // Get the PMPaper and check it is valid
    PMPaper macPaper = m_printDevice->macPaper(usePageSize);
    if (!macPaper) {
        qWarning() << "QMacPrintEngine: Invalid PMPaper returned for " << pageSize;
        return;
    }

    QMarginsF printable = m_printDevice->printableMargins(usePageSize, m_pageLayout.orientation(), resolution.hRes);
    m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units()));

    // You cannot set the page size on a PMPageFormat, you must create a new PMPageFormat
    PMPageFormat pageFormat;
    PMCreatePageFormatWithPMPaper(&pageFormat, macPaper);
    PMSetOrientation(pageFormat, m_pageLayout.orientation() == QPageLayout::Landscape ? kPMLandscape : kPMPortrait, kPMUnlocked);
    PMCopyPageFormat(pageFormat, format());
    if (PMSessionValidatePageFormat(session(), format(), kPMDontWantBoolean) != noErr)
        qWarning("QMacPrintEngine: Invalid page format");
    PMRelease(pageFormat);
}

void QMacPrintEngine::updateState(const QPaintEngineState &state)
{
    d_func()->paintEngine->updateState(state);
}

void QMacPrintEngine::drawRects(const QRectF *r, int num)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    d->paintEngine->drawRects(r, num);
}

void QMacPrintEngine::drawPoints(const QPointF *points, int pointCount)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    d->paintEngine->drawPoints(points, pointCount);
}

void QMacPrintEngine::drawEllipse(const QRectF &r)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    d->paintEngine->drawEllipse(r);
}

void QMacPrintEngine::drawLines(const QLineF *lines, int lineCount)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    d->paintEngine->drawLines(lines, lineCount);
}

void QMacPrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    d->paintEngine->drawPolygon(points, pointCount, mode);
}

void QMacPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    d->paintEngine->drawPixmap(r, pm, sr);
}

void QMacPrintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    d->paintEngine->drawImage(r, pm, sr, flags);
}

void QMacPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    if (!d->embedFonts)
        QPaintEngine::drawTextItem(p, ti);
    else
        d->paintEngine->drawTextItem(p, ti);
}

void QMacPrintEngine::drawTiledPixmap(const QRectF &dr, const QPixmap &pixmap, const QPointF &sr)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    d->paintEngine->drawTiledPixmap(dr, pixmap, sr);
}

void QMacPrintEngine::drawPath(const QPainterPath &path)
{
    Q_D(QMacPrintEngine);
    Q_ASSERT(d->state == QPrinter::Active);
    d->paintEngine->drawPath(path);
}


void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
{
    Q_D(QMacPrintEngine);

    d->valueCache.insert(key, value);
    if (!d->printInfo)
        return;

    switch (key) {

    // The following keys are properties or derived values and so cannot be set
    case PPK_PageRect:
        break;
    case PPK_PaperRect:
        break;
    case PPK_PaperSources:
        break;
    case PPK_SupportsMultipleCopies:
        break;
    case PPK_SupportedResolutions:
        break;

    // The following keys are settings that are unsupported by the Mac PrintEngine
    case PPK_ColorMode:
        break;
    case PPK_CustomBase:
        break;
    case PPK_PageOrder:
        // TODO Check if can be supported via Cups Options
        break;
    case PPK_PaperSource:
        // TODO Check if can be supported via Cups Options
        break;
    case PPK_PrinterProgram:
        break;
    case PPK_SelectionOption:
        break;

    // The following keys are properties and settings that are supported by the Mac PrintEngine
    case PPK_FontEmbedding:
        d->embedFonts = value.toBool();
        break;
    case PPK_Resolution:  {
        int bestResolution = 0;
        int dpi = value.toInt();
        int bestDistance = INT_MAX;
        for (int resolution : d->m_printDevice->supportedResolutions()) {
            if (dpi == resolution) {
                bestResolution = resolution;
                break;
            } else {
                int distance = qAbs(dpi - resolution);
                if (distance < bestDistance) {
                    bestDistance = distance;
                    bestResolution = resolution;
                }
            }
        }
        PMResolution resolution;
        resolution.hRes = resolution.vRes = bestResolution;
        if (PMPrinterSetOutputResolution(d->m_printDevice->macPrinter(), d->settings(), &resolution) == noErr) {
            // Setting the resolution succeeded.
            // Now try to read the actual resolution selected by the OS.
            if (PMPrinterGetOutputResolution(d->m_printDevice->macPrinter(), d->settings(), &d->resolution) != noErr) {
                // Reading the resolution somehow failed; d->resolution is in undefined state.
                // So use the value which was acceptable to PMPrinterSetOutputResolution.
                d->resolution = resolution;
            }
        }
        break;
    }
    case PPK_CollateCopies:
        PMSetCollate(d->settings(), value.toBool());
        break;
    case PPK_Creator:
        d->m_creator = value.toString();
        break;
    case PPK_DocumentName:
        PMPrintSettingsSetJobName(d->settings(), QCFString(value.toString()));
        break;
    case PPK_Duplex: {
        QPrint::DuplexMode mode = QPrint::DuplexMode(value.toInt());
        if (mode == property(PPK_Duplex).toInt() || !d->m_printDevice->supportedDuplexModes().contains(mode))
            break;
        switch (mode) {
        case QPrint::DuplexNone:
            PMSetDuplex(d->settings(), kPMDuplexNone);
            break;
        case QPrint::DuplexAuto:
            PMSetDuplex(d->settings(), d->m_pageLayout.orientation() == QPageLayout::Landscape ? kPMDuplexTumble : kPMDuplexNoTumble);
            break;
        case QPrint::DuplexLongSide:
            PMSetDuplex(d->settings(), kPMDuplexNoTumble);
            break;
        case QPrint::DuplexShortSide:
            PMSetDuplex(d->settings(), kPMDuplexTumble);
            break;
        default:
            // Don't change
            break;
        }
        break;
    }
    case PPK_FullPage:
        if (value.toBool())
            d->m_pageLayout.setMode(QPageLayout::FullPageMode);
        else
            d->m_pageLayout.setMode(QPageLayout::StandardMode);
        break;
    case PPK_CopyCount: // fallthrough
    case PPK_NumberOfCopies:
        PMSetCopies(d->settings(), value.toInt(), false);
        break;
    case PPK_Orientation: {
        // First try set the Mac format orientation, then set our orientation to match result
        QPageLayout::Orientation newOrientation = QPageLayout::Orientation(value.toInt());
        PMOrientation macOrientation = (newOrientation == QPageLayout::Landscape) ? kPMLandscape : kPMPortrait;
        PMSetOrientation(d->format(), macOrientation, kPMUnlocked);
        PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean);
        PMGetOrientation(d->format(), &macOrientation);
        d->m_pageLayout.setOrientation(macOrientation == kPMLandscape ? QPageLayout::Landscape : QPageLayout::Portrait);
        break;
    }
    case PPK_OutputFileName:
        d->outputFilename = value.toString();
        break;
    case PPK_PageSize:
        d->setPageSize(QPageSize(QPageSize::PageSizeId(value.toInt())));
        break;
    case PPK_PaperName:
        // Get the named page size from the printer if supported
        d->setPageSize(d->m_printDevice->supportedPageSize(value.toString()));
        break;
    case PPK_WindowsPageSize:
        d->setPageSize(QPageSize(QPageSize::id(value.toInt())));
        break;
    case PPK_PrinterName: {
        QVariant pageSize = QVariant::fromValue(d->m_pageLayout.pageSize());
        const bool isFullPage = d->m_pageLayout.mode() == QPageLayout::FullPageMode;
        QVariant orientation = QVariant::fromValue(d->m_pageLayout.orientation());
        QVariant margins = QVariant::fromValue(QPair<QMarginsF, QPageLayout::Unit>(d->m_pageLayout.margins(),
                                                                                   d->m_pageLayout.units()));
        QString id = value.toString();
        if (id.isEmpty())
            id = QCocoaPrinterSupport().defaultPrintDeviceId();
        else if (!QCocoaPrinterSupport().availablePrintDeviceIds().contains(id))
            break;
        d->m_printDevice.reset(new QCocoaPrintDevice(id));
        PMPrinter printer = d->m_printDevice->macPrinter();
        PMRetain(printer);
        PMSessionSetCurrentPMPrinter(d->session(), printer);
        // Ensure the settings are up to date and valid
        if (d->m_printDevice->supportedPageSize(pageSize.value<QPageSize>()).isValid())
            setProperty(PPK_QPageSize, pageSize);
        else
            setProperty(PPK_CustomPaperSize, pageSize.value<QPageSize>().size(QPageSize::Point));
        setProperty(PPK_FullPage, QVariant(isFullPage));
        setProperty(PPK_Orientation, orientation);
        setProperty(PPK_QPageMargins, margins);
        break;
    }
    case PPK_CustomPaperSize:
        d->setPageSize(QPageSize(value.toSizeF(), QPageSize::Point));
        break;
    case PPK_PageMargins:
    {
        QList<QVariant> margins(value.toList());
        Q_ASSERT(margins.size() == 4);
        d->m_pageLayout.setMargins(QMarginsF(margins.at(0).toReal(), margins.at(1).toReal(),
                                             margins.at(2).toReal(), margins.at(3).toReal()));
        break;
    }
    case PPK_QPageSize:
        d->setPageSize(value.value<QPageSize>());
        break;
    case PPK_QPageMargins: {
        QPair<QMarginsF, QPageLayout::Unit> pair = value.value<QPair<QMarginsF, QPageLayout::Unit> >();
        d->m_pageLayout.setUnits(pair.second);
        d->m_pageLayout.setMargins(pair.first);
        break;
    }
    case PPK_QPageLayout: {
        QPageLayout pageLayout = value.value<QPageLayout>();
        if (pageLayout.isValid() && d->m_printDevice->isValidPageLayout(pageLayout, d->resolution.hRes)) {
            setProperty(PPK_QPageSize, QVariant::fromValue(pageLayout.pageSize()));
            setProperty(PPK_FullPage, pageLayout.mode() == QPageLayout::FullPageMode);
            setProperty(PPK_Orientation, QVariant::fromValue(pageLayout.orientation()));
            d->m_pageLayout.setUnits(pageLayout.units());
            d->m_pageLayout.setMargins(pageLayout.margins());
        }
        break;
    }
    // No default so that compiler will complain if new keys added and not handled in this engine
    }
}

QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const
{
    Q_D(const QMacPrintEngine);
    QVariant ret;

    if (!d->printInfo && d->valueCache.contains(key))
        return *d->valueCache.find(key);

    switch (key) {

    // The following keys are settings that are unsupported by the Mac PrintEngine
    // Return sensible default values to ensure consistent behavior across platforms
    case PPK_ColorMode:
        ret = QPrinter::Color;
        break;
    case PPK_CustomBase:
        // Special case, leave null
        break;
    case PPK_PageOrder:
        // TODO Check if can be supported via Cups Options
        ret = QPrinter::FirstPageFirst;
        break;
    case PPK_PaperSource:
        // TODO Check if can be supported via Cups Options
        ret = QPrinter::Auto;
        break;
    case PPK_PaperSources: {
        // TODO Check if can be supported via Cups Options
        QList<QVariant> out;
        out << int(QPrinter::Auto);
        ret = out;
        break;
        }
    case PPK_PrinterProgram:
        ret = QString();
        break;
    case PPK_SelectionOption:
        ret = QString();
        break;

    // The following keys are properties and settings that are supported by the Mac PrintEngine
    case PPK_FontEmbedding:
        ret = d->embedFonts;
        break;
    case PPK_CollateCopies: {
        Boolean status;
        PMGetCollate(d->settings(), &status);
        ret = bool(status);
        break;
    }
    case PPK_Creator:
        ret = d->m_creator;
        break;
    case PPK_DocumentName: {
        CFStringRef name;
        PMPrintSettingsGetJobName(d->settings(), &name);
        ret = QString::fromCFString(name);
        break;
    }
    case PPK_Duplex: {
        PMDuplexMode mode = kPMDuplexNone;
        PMGetDuplex(d->settings(), &mode);
        switch (mode) {
        case kPMDuplexNoTumble:
            ret = QPrinter::DuplexLongSide;
            break;
        case kPMDuplexTumble:
            ret = QPrinter::DuplexShortSide;
            break;
        case kPMDuplexNone:
        default:
            ret = QPrinter::DuplexNone;
            break;
        }
        break;
    }
    case PPK_FullPage:
        ret = d->m_pageLayout.mode() == QPageLayout::FullPageMode;
        break;
    case PPK_NumberOfCopies:
        ret = 1;
        break;
    case PPK_CopyCount: {
        UInt32 copies = 1;
        PMGetCopies(d->settings(), &copies);
        ret = (uint) copies;
        break;
    }
    case PPK_SupportsMultipleCopies:
        ret = true;
        break;
    case PPK_Orientation:
        ret = d->m_pageLayout.orientation();
        break;
    case PPK_OutputFileName:
        ret = d->outputFilename;
        break;
    case PPK_PageRect:
        // PageRect is returned in device pixels
        ret = d->m_pageLayout.paintRectPixels(d->resolution.hRes);
        break;
    case PPK_PageSize:
        ret = d->m_pageLayout.pageSize().id();
        break;
    case PPK_PaperName:
        ret = d->m_pageLayout.pageSize().name();
        break;
    case PPK_WindowsPageSize:
        ret = d->m_pageLayout.pageSize().windowsId();
        break;
    case PPK_PaperRect:
        // PaperRect is returned in device pixels
        ret = d->m_pageLayout.fullRectPixels(d->resolution.hRes);
        break;
    case PPK_PrinterName:
        return d->m_printDevice->id();
        break;
    case PPK_Resolution: {
        ret = d->resolution.hRes;
        break;
    }
    case PPK_SupportedResolutions: {
        QList<QVariant> list;
        for (int resolution : d->m_printDevice->supportedResolutions())
            list << resolution;
        ret = list;
        break;
    }
    case PPK_CustomPaperSize:
        ret = d->m_pageLayout.fullRectPoints().size();
        break;
    case PPK_PageMargins: {
        QList<QVariant> list;
        QMarginsF margins = d->m_pageLayout.margins(QPageLayout::Point);
        list << margins.left() << margins.top() << margins.right() << margins.bottom();
        ret = list;
        break;
    }
    case PPK_QPageSize:
        ret.setValue(d->m_pageLayout.pageSize());
        break;
    case PPK_QPageMargins: {
        QPair<QMarginsF, QPageLayout::Unit> pair = qMakePair(d->m_pageLayout.margins(), d->m_pageLayout.units());
        ret.setValue(pair);
        break;
    }
    case PPK_QPageLayout:
        ret.setValue(d->m_pageLayout);
    // No default so that compiler will complain if new keys added and not handled in this engine
    }
    return ret;
}

QT_END_NAMESPACE

#endif // QT_NO_PRINTER
