File indexing completed on 2024-04-28 16:55:46

0001 /*
0002  * SPDX-FileCopyrightText: 2016-2017 Jan Grulich <jgrulich@redhat.com>
0003  * SPDX-FileCopyrightText: 2007, 2010 John Layt <john@layt.net>
0004  * SPDX-FileCopyrightText: 2007 Alex Merry <huntedhacker@tiscali.co.uk>
0005  * SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
0006  *
0007  * SPDX-License-Identifier: LGPL-2.0-or-later
0008  *
0009  */
0010 
0011 #include "print.h"
0012 #include "print_debug.h"
0013 #include "request.h"
0014 #include "utils.h"
0015 
0016 #include <KProcess>
0017 
0018 #include <QFile>
0019 #include <QPrintDialog>
0020 #include <QPrintEngine>
0021 #include <QStandardPaths>
0022 // #include <QtPrintSupport/private/qprint_p.h>
0023 #include <QTemporaryFile>
0024 #include <QUrl>
0025 #include <QtPrintSupport/private/qcups_p.h>
0026 
0027 // INFO: code below is copied from Qt as there is no public API for converting key to PageSizeId
0028 
0029 // Standard sizes data
0030 struct StandardPageSize {
0031     QPageSize::PageSizeId id;
0032     const char *mediaOption; // PPD standard mediaOption ID
0033 };
0034 
0035 // Standard page sizes taken from the Postscript PPD Standard v4.3
0036 // See https://www-cdf.fnal.gov/offline/PostScript/5003.PPD_Spec_v4.3.pdf
0037 // Excludes all Transverse and Rotated sizes
0038 // NB!This table needs to be in sync with QPageSize::PageSizeId
0039 const static StandardPageSize qt_pageSizes[] = {
0040 
0041     // Existing Qt sizes including ISO, US, ANSI and other standards
0042     {QPageSize::A4, "A4"},
0043     {QPageSize::B5, "ISOB5"},
0044     {QPageSize::Letter, "Letter"},
0045     {QPageSize::Legal, "Legal"},
0046     {QPageSize::Executive, "Executive.7.5x10in"}, // Qt size differs from Postscript / Windows
0047     {QPageSize::A0, "A0"},
0048     {QPageSize::A1, "A1"},
0049     {QPageSize::A2, "A2"},
0050     {QPageSize::A3, "A3"},
0051     {QPageSize::A5, "A5"},
0052     {QPageSize::A6, "A6"},
0053     {QPageSize::A7, "A7"},
0054     {QPageSize::A8, "A8"},
0055     {QPageSize::A9, "A9"},
0056     {QPageSize::B0, "ISOB0"},
0057     {QPageSize::B1, "ISOB1"},
0058     {QPageSize::B10, "ISOB10"},
0059     {QPageSize::B2, "ISOB2"},
0060     {QPageSize::B3, "ISOB3"},
0061     {QPageSize::B4, "ISOB4"},
0062     {QPageSize::B6, "ISOB6"},
0063     {QPageSize::B7, "ISOB7"},
0064     {QPageSize::B8, "ISOB8"},
0065     {QPageSize::B9, "ISOB9"},
0066     {QPageSize::C5E, "EnvC5"},
0067     {QPageSize::Comm10E, "Env10"},
0068     {QPageSize::DLE, "EnvDL"},
0069     {QPageSize::Folio, "Folio"},
0070     {QPageSize::Ledger, "Ledger"},
0071     {QPageSize::Tabloid, "Tabloid"},
0072     {QPageSize::Custom, "Custom"}, // Special case to keep in sync with QPageSize::PageSizeId
0073 
0074     // ISO Standard Sizes
0075     {QPageSize::A10, "A10"},
0076     {QPageSize::A3Extra, "A3Extra"},
0077     {QPageSize::A4Extra, "A4Extra"},
0078     {QPageSize::A4Plus, "A4Plus"},
0079     {QPageSize::A4Small, "A4Small"},
0080     {QPageSize::A5Extra, "A5Extra"},
0081     {QPageSize::B5Extra, "ISOB5Extra"},
0082 
0083     // JIS Standard Sizes
0084     {QPageSize::JisB0, "B0"},
0085     {QPageSize::JisB1, "B1"},
0086     {QPageSize::JisB2, "B2"},
0087     {QPageSize::JisB3, "B3"},
0088     {QPageSize::JisB4, "B4"},
0089     {QPageSize::JisB5, "B5"},
0090     {QPageSize::JisB6, "B6"},
0091     {QPageSize::JisB7, "B7"},
0092     {QPageSize::JisB8, "B8"},
0093     {QPageSize::JisB9, "B9"},
0094     {QPageSize::JisB10, "B10"},
0095 
0096     // ANSI / US Standard sizes
0097     {QPageSize::AnsiC, "AnsiC"},
0098     {QPageSize::AnsiD, "AnsiD"},
0099     {QPageSize::AnsiE, "AnsiE"},
0100     {QPageSize::LegalExtra, "LegalExtra"},
0101     {QPageSize::LetterExtra, "LetterExtra"},
0102     {QPageSize::LetterPlus, "LetterPlus"},
0103     {QPageSize::LetterSmall, "LetterSmall"},
0104     {QPageSize::TabloidExtra, "TabloidExtra"},
0105 
0106     // Architectural sizes
0107     {QPageSize::ArchA, "ARCHA"},
0108     {QPageSize::ArchB, "ARCHB"},
0109     {QPageSize::ArchC, "ARCHC"},
0110     {QPageSize::ArchD, "ARCHD"},
0111     {QPageSize::ArchE, "ARCHE"},
0112 
0113     // Inch-based Sizes
0114     {QPageSize::Imperial7x9, "7x9"},
0115     {QPageSize::Imperial8x10, "8x10"},
0116     {QPageSize::Imperial9x11, "9x11"},
0117     {QPageSize::Imperial9x12, "9x12"},
0118     {QPageSize::Imperial10x11, "10x11"},
0119     {QPageSize::Imperial10x13, "10x13"},
0120     {QPageSize::Imperial10x14, "10x14"},
0121     {QPageSize::Imperial12x11, "12x11"},
0122     {QPageSize::Imperial15x11, "15x11"},
0123 
0124     // Other Page Sizes
0125     {QPageSize::ExecutiveStandard, "Executive"}, // Qt size differs from Postscript / Windows
0126     {QPageSize::Note, "Note"},
0127     {QPageSize::Quarto, "Quarto"},
0128     {QPageSize::Statement, "Statement"},
0129     {QPageSize::SuperA, "SuperA"},
0130     {QPageSize::SuperB, "SuperB"},
0131     {QPageSize::Postcard, "Postcard"},
0132     {QPageSize::DoublePostcard, "DoublePostcard"},
0133     {QPageSize::Prc16K, "PRC16K"},
0134     {QPageSize::Prc32K, "PRC32K"},
0135     {QPageSize::Prc32KBig, "PRC32KBig"},
0136 
0137     // Fan Fold Sizes
0138     {QPageSize::FanFoldUS, "FanFoldUS"},
0139     {QPageSize::FanFoldGerman, "FanFoldGerman"},
0140     {QPageSize::FanFoldGermanLegal, "FanFoldGermanLegal"},
0141 
0142     // ISO Envelopes
0143     {QPageSize::EnvelopeB4, "EnvISOB4"},
0144     {QPageSize::EnvelopeB5, "EnvISOB5"},
0145     {QPageSize::EnvelopeB6, "EnvISOB6"},
0146     {QPageSize::EnvelopeC0, "EnvC0"},
0147     {QPageSize::EnvelopeC1, "EnvC1"},
0148     {QPageSize::EnvelopeC2, "EnvC2"},
0149     {QPageSize::EnvelopeC3, "EnvC3"},
0150     {QPageSize::EnvelopeC4, "EnvC4"},
0151     {QPageSize::EnvelopeC6, "EnvC6"},
0152     {QPageSize::EnvelopeC65, "EnvC65"},
0153     {QPageSize::EnvelopeC7, "EnvC7"},
0154 
0155     // US Envelopes
0156     {QPageSize::Envelope9, "Env9"},
0157     {QPageSize::Envelope11, "Env11"},
0158     {QPageSize::Envelope12, "Env12"},
0159     {QPageSize::Envelope14, "Env14"},
0160     {QPageSize::EnvelopeMonarch, "EnvMonarch"},
0161     {QPageSize::EnvelopePersonal, "EnvPersonal"},
0162 
0163     // Other Envelopes
0164     {QPageSize::EnvelopeChou3, "EnvChou3"},
0165     {QPageSize::EnvelopeChou4, "EnvChou4"},
0166     {QPageSize::EnvelopeInvite, "EnvInvite"},
0167     {QPageSize::EnvelopeItalian, "EnvItalian"},
0168     {QPageSize::EnvelopeKaku2, "EnvKaku2"},
0169     {QPageSize::EnvelopeKaku3, "EnvKaku3"},
0170     {QPageSize::EnvelopePrc1, "EnvPRC1"},
0171     {QPageSize::EnvelopePrc2, "EnvPRC2"},
0172     {QPageSize::EnvelopePrc3, "EnvPRC3"},
0173     {QPageSize::EnvelopePrc4, "EnvPRC4"},
0174     {QPageSize::EnvelopePrc5, "EnvPRC5"},
0175     {QPageSize::EnvelopePrc6, "EnvPRC6"},
0176     {QPageSize::EnvelopePrc7, "EnvPRC7"},
0177     {QPageSize::EnvelopePrc8, "EnvPRC8"},
0178     {QPageSize::EnvelopePrc9, "EnvPRC9"},
0179     {QPageSize::EnvelopePrc10, "EnvPRC10"},
0180     {QPageSize::EnvelopeYou4, "EnvYou4"}};
0181 
0182 // Return key name for PageSize
0183 static QString qt_keyForPageSizeId(QPageSize::PageSizeId id)
0184 {
0185     return QString::fromLatin1(qt_pageSizes[id].mediaOption);
0186 }
0187 
0188 // Return id name for PPD Key
0189 static QPageSize::PageSizeId qt_idForPpdKey(const QString &ppdKey)
0190 {
0191     if (ppdKey.isEmpty()) {
0192         return QPageSize::Custom;
0193     }
0194 
0195     for (int i = 0; i <= int(QPageSize::LastPageSize); ++i) {
0196         if (QLatin1String(qt_pageSizes[i].mediaOption) == ppdKey) {
0197             return qt_pageSizes[i].id;
0198         }
0199     }
0200     return QPageSize::Custom;
0201 }
0202 
0203 PrintPortal::PrintPortal(QObject *parent)
0204     : QDBusAbstractAdaptor(parent)
0205 {
0206 }
0207 
0208 PrintPortal::~PrintPortal()
0209 {
0210 }
0211 
0212 uint PrintPortal::Print(const QDBusObjectPath &handle,
0213                         const QString &app_id,
0214                         const QString &parent_window,
0215                         const QString &title,
0216                         const QDBusUnixFileDescriptor &fd,
0217                         const QVariantMap &options,
0218                         QVariantMap &results)
0219 {
0220     Q_UNUSED(results)
0221     qCDebug(XdgDesktopPortalKdePrint) << "Print called with parameters:";
0222     qCDebug(XdgDesktopPortalKdePrint) << "    handle: " << handle.path();
0223     qCDebug(XdgDesktopPortalKdePrint) << "    app_id: " << app_id;
0224     qCDebug(XdgDesktopPortalKdePrint) << "    parent_window: " << parent_window;
0225     qCDebug(XdgDesktopPortalKdePrint) << "    title: " << title;
0226     qCDebug(XdgDesktopPortalKdePrint) << "    fd: " << fd.fileDescriptor();
0227     qCDebug(XdgDesktopPortalKdePrint) << "    options: " << options;
0228 
0229     QFile fileToPrint;
0230     if (fileToPrint.open(fd.fileDescriptor(), QIODevice::ReadOnly)) {
0231         QPrinter *printer = nullptr;
0232         // Use printer associated with token if possible
0233         if (options.contains(QStringLiteral("token"))) {
0234             printer = m_printers.value(options.value(QStringLiteral("token")).toUInt());
0235         } else {
0236             // Use the last configured printer otherwise
0237             if (m_printers.count()) {
0238                 printer = m_printers.last();
0239             }
0240         }
0241 
0242         if (!printer) {
0243             qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: no QPrinter what can be used for printing";
0244             return 1;
0245         }
0246 
0247         // We are going to print to a file
0248         if (!printer->outputFileName().isEmpty()) {
0249             if (QFile::exists(printer->outputFileName())) {
0250                 QFile::remove(printer->outputFileName());
0251             }
0252 
0253             QByteArray pdfContent = fileToPrint.readAll();
0254             QFile outputFile(printer->outputFileName());
0255             if (outputFile.open(QIODevice::ReadWrite)) {
0256                 outputFile.write(pdfContent);
0257                 outputFile.close();
0258             } else {
0259                 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: couldn't open the file for writing";
0260             }
0261 
0262             fileToPrint.close();
0263 
0264             return 0;
0265 
0266             // TODO poscript support?
0267 
0268             // Print to a printer via lpr command
0269         } else {
0270             // The code below is copied from Okular
0271             bool useCupsOptions = cupsAvailable();
0272             QString exe;
0273             QStringList argList;
0274 
0275             // Decide what executable to use to print with, need the CUPS version of lpr if available
0276             // Some distros name the CUPS version of lpr as lpr-cups or lpr.cups so try those first
0277             // before default to lpr, or failing that to lp
0278             if (!QStandardPaths::findExecutable(QStringLiteral("lpr-cups")).isEmpty()) {
0279                 exe = QStringLiteral("lpr-cups");
0280             } else if (!QStandardPaths::findExecutable(QStringLiteral("lpr.cups")).isEmpty()) {
0281                 exe = QStringLiteral("lpr.cups");
0282             } else if (!QStandardPaths::findExecutable(QStringLiteral("lpr")).isEmpty()) {
0283                 exe = QStringLiteral("lpr");
0284             } else if (!QStandardPaths::findExecutable(QStringLiteral("lp")).isEmpty()) {
0285                 exe = QStringLiteral("lp");
0286             } else {
0287                 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: couldn't run lpr command for printing";
0288                 return 1;
0289             }
0290 
0291             QTemporaryFile tempFile;
0292             if (tempFile.open()) {
0293                 tempFile.write(fileToPrint.readAll());
0294                 tempFile.close();
0295             } else {
0296                 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: couldn't create temporary file for printing";
0297                 return 1;
0298             }
0299 
0300             argList = printArguments(printer, useCupsOptions, exe, printer->pageLayout().orientation()) << tempFile.fileName();
0301             // qCDebug(XdgDesktopPortalKdePrint) << "Executing" << exe << "with arguments" << argList << tempFile.fileName();
0302             int retValue = KProcess::execute(exe, argList);
0303 
0304             if (retValue <= 0) {
0305                 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: running KProcess failed";
0306                 return 1;
0307             }
0308 
0309             return retValue;
0310         }
0311     } else {
0312         qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: couldn't not read from fd";
0313         return 1;
0314     }
0315 
0316     return 0;
0317 }
0318 
0319 uint PrintPortal::PreparePrint(const QDBusObjectPath &handle,
0320                                const QString &app_id,
0321                                const QString &parent_window,
0322                                const QString &title,
0323                                const QVariantMap &settings,
0324                                const QVariantMap &page_setup,
0325                                const QVariantMap &options,
0326                                QVariantMap &results)
0327 {
0328     qCDebug(XdgDesktopPortalKdePrint) << "PreparePrint called with parameters:";
0329     qCDebug(XdgDesktopPortalKdePrint) << "    handle: " << handle.path();
0330     qCDebug(XdgDesktopPortalKdePrint) << "    app_id: " << app_id;
0331     qCDebug(XdgDesktopPortalKdePrint) << "    parent_window: " << parent_window;
0332     qCDebug(XdgDesktopPortalKdePrint) << "    title: " << title;
0333     qCDebug(XdgDesktopPortalKdePrint) << "    settings: " << settings;
0334     qCDebug(XdgDesktopPortalKdePrint) << "    page_setup: " << page_setup;
0335     qCDebug(XdgDesktopPortalKdePrint) << "    options: " << options;
0336 
0337     // Create new one
0338     QPrinter *printer = new QPrinter();
0339 
0340     // First we have to load pre-configured options
0341 
0342     // Process settings (used by printer)
0343     QCUPSSupport::PagesPerSheet pagesPerSheet = QCUPSSupport::OnePagePerSheet;
0344     QCUPSSupport::PagesPerSheetLayout pagesPerSheetLayout = QCUPSSupport::LeftToRightTopToBottom;
0345     for (auto it = settings.constBegin(); it != settings.constEnd(); ++it) {
0346         if (it.key() == QStringLiteral("orientation")) {
0347             const QString orientation = it.value().toString();
0348             if (orientation == QLatin1String("landscape") || orientation == QLatin1String("reverse_landscape")) {
0349                 printer->setPageOrientation(QPageLayout::Landscape);
0350             } else if (orientation == QLatin1String("portrait") || orientation == QLatin1String("reverse_portrait")) {
0351                 printer->setPageOrientation(QPageLayout::Portrait);
0352             }
0353         } else if (it.key() == QStringLiteral("n-copies")) {
0354             printer->setCopyCount(it.value().toString().toInt());
0355         } else if (it.key() == QStringLiteral("resolution")) {
0356             printer->setResolution(it.value().toString().toInt());
0357         } else if (it.key() == QStringLiteral("use-color")) {
0358             printer->setColorMode(it.value().toString() == QLatin1String("yes") ? QPrinter::Color : QPrinter::GrayScale);
0359         } else if (it.key() == QStringLiteral("duplex")) {
0360             const QString duplex = it.value().toString();
0361             if (duplex == QLatin1String("simplex")) {
0362                 printer->setDuplex(QPrinter::DuplexNone);
0363             } else if (duplex == QLatin1String("horizontal")) {
0364                 printer->setDuplex(QPrinter::DuplexShortSide);
0365             } else if (duplex == QLatin1String("vertical")) {
0366                 printer->setDuplex(QPrinter::DuplexLongSide);
0367             }
0368         } else if (it.key() == QStringLiteral("collate")) {
0369             printer->setCollateCopies(it.value().toString() == QLatin1String("yes"));
0370         } else if (it.key() == QStringLiteral("reverse")) {
0371             printer->setPageOrder(it.value().toString() == QLatin1String("yes") ? QPrinter::LastPageFirst : QPrinter::FirstPageFirst);
0372         } else if (it.key() == QStringLiteral("print-pages")) {
0373             const QString printPages = it.value().toString();
0374             if (printPages == QLatin1String("all")) {
0375                 printer->setPrintRange(QPrinter::AllPages);
0376             } else if (printPages == QLatin1String("selection")) {
0377                 printer->setPrintRange(QPrinter::Selection);
0378             } else if (printPages == QLatin1String("current")) {
0379                 printer->setPrintRange(QPrinter::CurrentPage);
0380             } else if (printPages == QLatin1String("ranges")) {
0381                 printer->setPrintRange(QPrinter::PageRange);
0382             }
0383         } else if (it.key() == QStringLiteral("page-ranges")) {
0384             const QString range = it.value().toString();
0385             // Gnome supports format like 1-5,7,9,11-15, however Qt support only e.g.1-15
0386             // so get the first and the last value
0387             const QStringList ranges = range.split(QLatin1Char(','));
0388             if (ranges.count()) {
0389                 QStringList firstRangeValues = ranges.first().split(QLatin1Char('-'));
0390                 QStringList lastRangeValues = ranges.last().split(QLatin1Char('-'));
0391                 printer->setFromTo(firstRangeValues.first().toInt(), lastRangeValues.last().toInt());
0392             }
0393         } else if (it.key() == QStringLiteral("page-set")) {
0394             // WARNING Qt internal private API, anyway the print dialog doesn't seem to
0395             // read these properties, but I'll leave it here in case this changes in future
0396             const QString pageSet = it.value().toString();
0397             if (pageSet == QLatin1String("all")) {
0398                 QCUPSSupport::setPageSet(printer, QCUPSSupport::AllPages);
0399             } else if (pageSet == QLatin1String("even")) {
0400                 QCUPSSupport::setPageSet(printer, QCUPSSupport::EvenPages);
0401             } else if (pageSet == QLatin1String("odd")) {
0402                 QCUPSSupport::setPageSet(printer, QCUPSSupport::OddPages);
0403             }
0404         } else if (it.key() == QStringLiteral("number-up")) {
0405             // WARNING Qt internal private API, anyway the print dialog doesn't seem to
0406             // read these properties, but I'll leave it here in case this changes in future
0407             const QString numberUp = it.value().toString();
0408             if (numberUp == QLatin1String("1")) {
0409                 pagesPerSheet = QCUPSSupport::OnePagePerSheet;
0410             } else if (numberUp == QLatin1String("2")) {
0411                 pagesPerSheet = QCUPSSupport::TwoPagesPerSheet;
0412             } else if (numberUp == QLatin1String("4")) {
0413                 pagesPerSheet = QCUPSSupport::FourPagesPerSheet;
0414             } else if (numberUp == QLatin1String("6")) {
0415                 pagesPerSheet = QCUPSSupport::SixPagesPerSheet;
0416             } else if (numberUp == QLatin1String("9")) {
0417                 pagesPerSheet = QCUPSSupport::NinePagesPerSheet;
0418             } else if (numberUp == QLatin1String("16")) {
0419                 pagesPerSheet = QCUPSSupport::SixteenPagesPerSheet;
0420             }
0421         } else if (it.key() == QStringLiteral("number-up-layout")) {
0422             // WARNING Qt internal private API, anyway the print dialog doesn't seem to
0423             // read these properties, but I'll leave it here in case this changes in future
0424             const QString layout = it.value().toString();
0425             if (layout == QLatin1String("lrtb")) {
0426                 pagesPerSheetLayout = QCUPSSupport::LeftToRightTopToBottom;
0427             } else if (layout == QLatin1String("lrbt")) {
0428                 pagesPerSheetLayout = QCUPSSupport::LeftToRightBottomToTop;
0429             } else if (layout == QLatin1String("rltb")) {
0430                 pagesPerSheetLayout = QCUPSSupport::RightToLeftTopToBottom;
0431             } else if (layout == QLatin1String("rlbt")) {
0432                 pagesPerSheetLayout = QCUPSSupport::RightToLeftBottomToTop;
0433             } else if (layout == QLatin1String("tblr")) {
0434                 pagesPerSheetLayout = QCUPSSupport::TopToBottomLeftToRight;
0435             } else if (layout == QLatin1String("tbrl")) {
0436                 pagesPerSheetLayout = QCUPSSupport::TopToBottomRightToLeft;
0437             } else if (layout == QLatin1String("btlr")) {
0438                 pagesPerSheetLayout = QCUPSSupport::BottomToTopLeftToRight;
0439             } else if (layout == QLatin1String("btrl")) {
0440                 pagesPerSheetLayout = QCUPSSupport::BottomToTopRightToLeft;
0441             }
0442         } else if (it.key() == QStringLiteral("output-file-format")) {
0443             // TODO only PDF supported by Qt so when printing to file we can use PDF only
0444             // btw. this should be already set automatically because we set output file name
0445             printer->setOutputFormat(QPrinter::PdfFormat);
0446         } else if (it.key() == QStringLiteral("output-uri")) {
0447             const QUrl uri = QUrl(it.value().toString());
0448             // Check whether the uri is not just a directory name and whether we don't need to
0449             // append output-basename
0450             if (settings.contains(QStringLiteral("output-basename"))) {
0451                 const QString basename = settings.value(QStringLiteral("output-basename")).toString();
0452                 if (!uri.toDisplayString().endsWith(basename) && uri.toDisplayString().endsWith(QLatin1Char('/'))) {
0453                     printer->setOutputFileName(uri.toDisplayString() + basename);
0454                 }
0455             } else {
0456                 printer->setOutputFileName(uri.toDisplayString());
0457             }
0458         } else {
0459             qCDebug(XdgDesktopPortalKdePrint) << "Unhandled printer setting: " << it.key();
0460         }
0461     }
0462 
0463     QCUPSSupport::setPagesPerSheetLayout(printer, pagesPerSheet, pagesPerSheetLayout);
0464 
0465     // Process page setup
0466     QSizeF paperSize;
0467     QString ppdName;
0468     QString name;
0469     QMarginsF pageMargins = printer->pageLayout().margins(QPageLayout::Millimeter);
0470     for (auto it = page_setup.constBegin(); it != page_setup.constEnd(); ++it) {
0471         if (it.key() == QStringLiteral("PPDName")) {
0472             ppdName = it.value().toString();
0473         } else if (it.key() == QStringLiteral("Name")) {
0474             name = it.value().toString();
0475         }
0476 
0477         if (it.key() == QStringLiteral("Width")) {
0478             paperSize.setWidth(it.value().toReal());
0479         } else if (it.key() == QStringLiteral("Height")) {
0480             paperSize.setHeight(it.value().toReal());
0481         } else if (it.key() == QStringLiteral("MarginTop")) {
0482             pageMargins.setTop(it.value().toReal());
0483         } else if (it.key() == QStringLiteral("MarginBottom")) {
0484             pageMargins.setBottom(it.value().toReal());
0485         } else if (it.key() == QStringLiteral("MarginLeft")) {
0486             pageMargins.setLeft(it.value().toReal());
0487         } else if (it.key() == QStringLiteral("MarginRight")) {
0488             pageMargins.setRight(it.value().toReal());
0489         } else if (it.key() == QStringLiteral("Orientation")) {
0490             const QString orientation = it.value().toString();
0491             if (orientation == QLatin1String("landscape") || orientation == QLatin1String("reverse_landscape")) {
0492                 printer->setPageOrientation(QPageLayout::Landscape);
0493             } else if (orientation == QLatin1String("portrait") || orientation == QLatin1String("reverse_portrait")) {
0494                 printer->setPageOrientation(QPageLayout::Portrait);
0495             }
0496         } else {
0497             qCDebug(XdgDesktopPortalKdePrint) << "Unhandled page setup: " << it.key();
0498         }
0499     }
0500 
0501     if (!ppdName.isEmpty()) {
0502         printer->setPageSize(QPageSize(qt_idForPpdKey(ppdName)));
0503     } else if (!name.isEmpty()) {
0504         printer->setPageSize(QPageSize(paperSize, QPageSize::Millimeter, name));
0505     }
0506     printer->setPageMargins(pageMargins, QPageLayout::Millimeter);
0507 
0508     QPrintDialog *printDialog = new QPrintDialog(printer);
0509     Utils::setParentWindow(printDialog, parent_window);
0510     Request::makeClosableDialogRequest(handle, printDialog);
0511 
0512     // Process options
0513     if (options.contains(QStringLiteral("modal"))) {
0514         printDialog->setModal(options.value(QStringLiteral("modal")).toBool());
0515     }
0516 
0517     // Pass back what we configured
0518     if (printDialog->exec() == QDialog::Accepted) {
0519         QVariantMap resultingSettings;
0520         QVariantMap resultingPageSetup;
0521 
0522         // Process back printer settings
0523         resultingSettings.insert(QStringLiteral("n-copies"), QString::number(printer->copyCount()));
0524         resultingSettings.insert(QStringLiteral("resolution"), QString::number(printer->resolution()));
0525         resultingSettings.insert(QStringLiteral("use-color"), printer->colorMode() == QPrinter::Color ? QLatin1String("yes") : QLatin1String("no"));
0526         if (printer->duplex() == QPrinter::DuplexNone) {
0527             resultingSettings.insert(QStringLiteral("duplex"), QLatin1String("simplex"));
0528         } else if (printer->duplex() == QPrinter::DuplexShortSide) {
0529             resultingSettings.insert(QStringLiteral("duplex"), QLatin1String("horizontal"));
0530         } else if (printer->duplex() == QPrinter::DuplexLongSide) {
0531             resultingSettings.insert(QStringLiteral("duplex"), QLatin1String("vertical"));
0532         }
0533         resultingSettings.insert(QStringLiteral("collate"), printer->collateCopies() ? QLatin1String("yes") : QLatin1String("no"));
0534         resultingSettings.insert(QStringLiteral("reverse"), printer->pageOrder() == QPrinter::LastPageFirst ? QLatin1String("yes") : QLatin1String("no"));
0535         if (printer->printRange() == QPrinter::AllPages) {
0536             resultingSettings.insert(QStringLiteral("print-pages"), QLatin1String("all"));
0537         } else if (printer->printRange() == QPrinter::Selection) {
0538             resultingSettings.insert(QStringLiteral("print-pages"), QLatin1String("selection"));
0539         } else if (printer->printRange() == QPrinter::CurrentPage) {
0540             resultingSettings.insert(QStringLiteral("print-pages"), QLatin1String("current"));
0541         } else if (printer->printRange() == QPrinter::PageRange) {
0542             resultingSettings.insert(QStringLiteral("print-pages"), QLatin1String("ranges"));
0543             const int fromPageToIndex = printer->fromPage() ? printer->fromPage() - 1 : printer->fromPage();
0544             const int toPageToIndex = printer->toPage() ? printer->toPage() - 1 : printer->toPage();
0545             resultingSettings.insert(QStringLiteral("page-ranges"), QStringLiteral("%1-%2").arg(fromPageToIndex).arg(toPageToIndex));
0546         }
0547         // Set cups specific properties
0548         const QStringList cupsOptions = printer->printEngine()->property(PPK_CupsOptions).toStringList();
0549         qCDebug(XdgDesktopPortalKdePrint) << cupsOptions;
0550         if (cupsOptions.contains(QLatin1String("page-set"))) {
0551             resultingSettings.insert(QStringLiteral("page-set"), cupsOptions.at(cupsOptions.indexOf(QStringLiteral("page-set")) + 1));
0552         }
0553         if (cupsOptions.contains(QLatin1String("number-up"))) {
0554             resultingSettings.insert(QStringLiteral("number-up"), cupsOptions.at(cupsOptions.indexOf(QStringLiteral("number-up")) + 1));
0555         }
0556         if (cupsOptions.contains(QLatin1String("number-up-layout"))) {
0557             resultingSettings.insert(QStringLiteral("number-up-layout"), cupsOptions.at(cupsOptions.indexOf(QStringLiteral("number-up-layout")) + 1));
0558         }
0559 
0560         if (printer->outputFormat() == QPrinter::PdfFormat) {
0561             resultingSettings.insert(QStringLiteral("output-file-format"), QLatin1String("pdf"));
0562         }
0563 
0564         if (!printer->outputFileName().isEmpty()) {
0565             resultingSettings.insert(QStringLiteral("output-uri"), QUrl::fromLocalFile(printer->outputFileName()).toDisplayString());
0566         }
0567 
0568         // Process back page setup
0569         if (printer->pageLayout().pageSize().id() == QPageSize::Custom) {
0570             resultingPageSetup.insert(QStringLiteral("Name"), printer->pageLayout().pageSize().name());
0571             resultingPageSetup.insert(QStringLiteral("DisplayName"), printer->pageLayout().pageSize().name());
0572         } else {
0573             resultingPageSetup.insert(QStringLiteral("PPDName"), qt_keyForPageSizeId(printer->pageLayout().pageSize().id()));
0574             resultingPageSetup.insert(QStringLiteral("DisplayName"), qt_keyForPageSizeId(printer->pageLayout().pageSize().id()));
0575         }
0576         resultingPageSetup.insert(QStringLiteral("Width"), printer->pageLayout().pageSize().size(QPageSize::Millimeter).width());
0577         resultingPageSetup.insert(QStringLiteral("Height"), printer->pageLayout().pageSize().size(QPageSize::Millimeter).height());
0578         resultingPageSetup.insert(QStringLiteral("MarginTop"), printer->pageLayout().margins(QPageLayout::Millimeter).top());
0579         resultingPageSetup.insert(QStringLiteral("MarginBottom"), printer->pageLayout().margins(QPageLayout::Millimeter).bottom());
0580         resultingPageSetup.insert(QStringLiteral("MarginLeft"), printer->pageLayout().margins(QPageLayout::Millimeter).left());
0581         resultingPageSetup.insert(QStringLiteral("MarginRight"), printer->pageLayout().margins(QPageLayout::Millimeter).right());
0582         resultingPageSetup.insert(QStringLiteral("Orientation"),
0583                                   printer->pageLayout().orientation() == QPageLayout::Landscape ? QLatin1String("landscape") : QLatin1String("portrait"));
0584 
0585         qCDebug(XdgDesktopPortalKdePrint) << "Settings: ";
0586         qCDebug(XdgDesktopPortalKdePrint) << "---------------------------";
0587         qCDebug(XdgDesktopPortalKdePrint) << resultingSettings;
0588         qCDebug(XdgDesktopPortalKdePrint) << "Page setup: ";
0589         qCDebug(XdgDesktopPortalKdePrint) << "---------------------------";
0590         qCDebug(XdgDesktopPortalKdePrint) << resultingPageSetup;
0591 
0592         uint token = QDateTime::currentDateTime().toSecsSinceEpoch();
0593         results.insert(QStringLiteral("settings"), resultingSettings);
0594         results.insert(QStringLiteral("page-setup"), resultingPageSetup);
0595         results.insert(QStringLiteral("token"), token);
0596 
0597         m_printers.insert(token, printer);
0598 
0599         printDialog->deleteLater();
0600         return 0;
0601     } else {
0602         printDialog->deleteLater();
0603         return 1;
0604     }
0605 
0606     return 0;
0607 }
0608 
0609 QStringList PrintPortal::destination(const QPrinter *printer, const QString &version)
0610 {
0611     if (version == QLatin1String("lp")) {
0612         return QStringList(QStringLiteral("-d")) << printer->printerName();
0613     }
0614 
0615     if (version.startsWith(QLatin1String("lpr"))) {
0616         return QStringList(QStringLiteral("-P")) << printer->printerName();
0617     }
0618 
0619     return QStringList();
0620 }
0621 
0622 QStringList PrintPortal::copies(const QPrinter *printer, const QString &version)
0623 {
0624     int cp = printer->copyCount();
0625 
0626     if (version == QLatin1String("lp")) {
0627         return QStringList(QStringLiteral("-n")) << QStringLiteral("%1").arg(cp);
0628     }
0629 
0630     if (version.startsWith(QLatin1String("lpr"))) {
0631         return QStringList() << QStringLiteral("-#%1").arg(cp);
0632     }
0633 
0634     return QStringList();
0635 }
0636 
0637 QStringList PrintPortal::jobname(const QPrinter *printer, const QString &version)
0638 {
0639     if (!printer->docName().isEmpty()) {
0640         if (version == QLatin1String("lp")) {
0641             return QStringList(QStringLiteral("-t")) << printer->docName();
0642         }
0643 
0644         if (version.startsWith(QLatin1String("lpr"))) {
0645             const QString shortenedDocName = QString::fromUtf8(printer->docName().toUtf8().left(255));
0646             return QStringList(QStringLiteral("-J")) << shortenedDocName;
0647         }
0648     }
0649 
0650     return QStringList();
0651 }
0652 
0653 // What about Upper and MultiPurpose?  And others in PPD???
0654 QString PrintPortal::mediaPaperSource(const QPrinter *printer)
0655 {
0656     switch (printer->paperSource()) {
0657     case QPrinter::Auto:
0658         return QString();
0659     case QPrinter::Cassette:
0660         return QStringLiteral("Cassette");
0661     case QPrinter::Envelope:
0662         return QStringLiteral("Envelope");
0663     case QPrinter::EnvelopeManual:
0664         return QStringLiteral("EnvelopeManual");
0665     case QPrinter::FormSource:
0666         return QStringLiteral("FormSource");
0667     case QPrinter::LargeCapacity:
0668         return QStringLiteral("LargeCapacity");
0669     case QPrinter::LargeFormat:
0670         return QStringLiteral("LargeFormat");
0671     case QPrinter::Lower:
0672         return QStringLiteral("Lower");
0673     case QPrinter::MaxPageSource:
0674         return QStringLiteral("MaxPageSource");
0675     case QPrinter::Middle:
0676         return QStringLiteral("Middle");
0677     case QPrinter::Manual:
0678         return QStringLiteral("Manual");
0679     case QPrinter::OnlyOne:
0680         return QStringLiteral("OnlyOne");
0681     case QPrinter::Tractor:
0682         return QStringLiteral("Tractor");
0683     case QPrinter::SmallFormat:
0684         return QStringLiteral("SmallFormat");
0685     default:
0686         return QString();
0687     }
0688 }
0689 
0690 QStringList PrintPortal::optionOrientation(const QPrinter *printer, QPageLayout::Orientation documentOrientation)
0691 {
0692     // portrait and landscape options rotate the document according to the document orientation
0693     // If we want to print a landscape document as one would expect it, we have to pass the
0694     // portrait option so that the document is not rotated additionally
0695     if (printer->pageLayout().orientation() == documentOrientation) {
0696         // the user wants the document printed as is
0697         return QStringList(QStringLiteral("-o")) << QStringLiteral("portrait");
0698     } else {
0699         // the user expects the document being rotated by 90 degrees
0700         return QStringList(QStringLiteral("-o")) << QStringLiteral("landscape");
0701     }
0702 }
0703 
0704 QStringList PrintPortal::optionDoubleSidedPrinting(const QPrinter *printer)
0705 {
0706     switch (printer->duplex()) {
0707     case QPrinter::DuplexNone:
0708         return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=one-sided");
0709     case QPrinter::DuplexAuto:
0710         if (printer->pageLayout().orientation() == QPageLayout::Landscape) {
0711             return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-short-edge");
0712         } else {
0713             return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-long-edge");
0714         }
0715     case QPrinter::DuplexLongSide:
0716         return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-long-edge");
0717     case QPrinter::DuplexShortSide:
0718         return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-short-edge");
0719     default:
0720         return QStringList(); // Use printer default
0721     }
0722 }
0723 
0724 QStringList PrintPortal::optionPageOrder(const QPrinter *printer)
0725 {
0726     if (printer->pageOrder() == QPrinter::LastPageFirst) {
0727         return QStringList(QStringLiteral("-o")) << QStringLiteral("outputorder=reverse");
0728     }
0729     return QStringList(QStringLiteral("-o")) << QStringLiteral("outputorder=normal");
0730 }
0731 
0732 QStringList PrintPortal::optionCollateCopies(const QPrinter *printer)
0733 {
0734     if (printer->collateCopies()) {
0735         return QStringList(QStringLiteral("-o")) << QStringLiteral("Collate=True");
0736     }
0737     return QStringList(QStringLiteral("-o")) << QStringLiteral("Collate=False");
0738 }
0739 
0740 QStringList PrintPortal::optionPageMargins(const QPrinter *printer)
0741 {
0742     if (printer->printEngine()->property(QPrintEngine::PPK_PageMargins).isNull()) {
0743         return QStringList();
0744     } else {
0745         qreal l, t, r, b;
0746         QMarginsF m = printer->pageLayout().margins();
0747         l = m.left();
0748         t = m.top();
0749         r = m.right();
0750         b = m.bottom();
0751         return QStringList{
0752             QStringLiteral("-o"),
0753             QStringLiteral("page-left=%1").arg(l),
0754             QStringLiteral("-o"),
0755             QStringLiteral("page-top=%1").arg(t),
0756             QStringLiteral("-o"),
0757             QStringLiteral("page-right=%1").arg(r),
0758             QStringLiteral("-o"),
0759             QStringLiteral("page-bottom=%1").arg(b),
0760             QStringLiteral("-o"),
0761             QStringLiteral("fit-to-page"),
0762         };
0763     }
0764 }
0765 
0766 QStringList PrintPortal::optionCupsProperties(const QPrinter *printer)
0767 {
0768     QStringList dialogOptions = printer->printEngine()->property(QPrintEngine::PrintEnginePropertyKey(0xfe00)).toStringList();
0769     QStringList cupsOptions;
0770 
0771     for (int i = 0; i < dialogOptions.count(); i = i + 2) {
0772         // Ignore some cups properties as the pdf we get is already formatted using these
0773         if (dialogOptions[i] == QLatin1String("number-up") || dialogOptions[i] == QLatin1String("number-up-layout")) {
0774             continue;
0775         }
0776 
0777         if (dialogOptions[i + 1].isEmpty()) {
0778             cupsOptions << QStringLiteral("-o") << dialogOptions[i];
0779         } else {
0780             cupsOptions << QStringLiteral("-o") << dialogOptions[i] + QLatin1Char('=') + dialogOptions[i + 1];
0781         }
0782     }
0783 
0784     return cupsOptions;
0785 }
0786 
0787 QStringList PrintPortal::optionMedia(const QPrinter *printer)
0788 {
0789     if (!qt_keyForPageSizeId(printer->pageLayout().pageSize().id()).isEmpty() && !mediaPaperSource(printer).isEmpty()) {
0790         return QStringList(QStringLiteral("-o"))
0791             << QStringLiteral("media=%1,%2").arg(qt_keyForPageSizeId(printer->pageLayout().pageSize().id()), mediaPaperSource(printer));
0792     }
0793 
0794     if (!qt_keyForPageSizeId(printer->pageLayout().pageSize().id()).isEmpty()) {
0795         return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1").arg(qt_keyForPageSizeId(printer->pageLayout().pageSize().id()));
0796     }
0797 
0798     if (!mediaPaperSource(printer).isEmpty()) {
0799         return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1").arg(mediaPaperSource(printer));
0800     }
0801 
0802     return QStringList();
0803 }
0804 
0805 QStringList PrintPortal::pages(const QPrinter *printer, bool useCupsOptions, const QString &version)
0806 {
0807     if (printer->printRange() == QPrinter::PageRange) {
0808         if (version == QLatin1String("lp")) {
0809             return QStringList(QStringLiteral("-P")) << QStringLiteral("%1-%2").arg(printer->fromPage()).arg(printer->toPage());
0810         }
0811 
0812         if (version.startsWith(QLatin1String("lpr")) && useCupsOptions) {
0813             return QStringList(QStringLiteral("-o")) << QStringLiteral("page-ranges=%1-%2").arg(printer->fromPage()).arg(printer->toPage());
0814         }
0815     }
0816 
0817     return QStringList(); // AllPages
0818 }
0819 
0820 QStringList PrintPortal::cupsOptions(const QPrinter *printer, QPageLayout::Orientation documentOrientation)
0821 {
0822     Q_UNUSED(documentOrientation)
0823     QStringList optionList;
0824 
0825     // if (!optionMedia(printer).isEmpty()) {
0826     //     optionList << optionMedia(printer);
0827     // }
0828 
0829     // if (!optionOrientation(printer, documentOrientation).isEmpty()) {
0830     //     optionList << optionOrientation(printer, documentOrientation);
0831     // }
0832 
0833     if (!optionDoubleSidedPrinting(printer).isEmpty()) {
0834         optionList << optionDoubleSidedPrinting(printer);
0835     }
0836 
0837     if (!optionPageOrder(printer).isEmpty()) {
0838         optionList << optionPageOrder(printer);
0839     }
0840 
0841     if (!optionCollateCopies(printer).isEmpty()) {
0842         optionList << optionCollateCopies(printer);
0843     }
0844 
0845     // if (!optionPageMargins(printer).isEmpty()) {
0846     //     optionList << optionPageMargins(printer);
0847     // }
0848 
0849     optionList << optionCupsProperties(printer);
0850 
0851     return optionList;
0852 }
0853 
0854 QStringList PrintPortal::printArguments(const QPrinter *printer, bool useCupsOptions, const QString &version, QPageLayout::Orientation documentOrientation)
0855 {
0856     QStringList argList;
0857 
0858     if (!destination(printer, version).isEmpty()) {
0859         argList << destination(printer, version);
0860     }
0861 
0862     if (!copies(printer, version).isEmpty()) {
0863         argList << copies(printer, version);
0864     }
0865 
0866     if (!jobname(printer, version).isEmpty()) {
0867         argList << jobname(printer, version);
0868     }
0869 
0870     // if (!pages(printer, useCupsOptions, version).isEmpty()) {
0871     //     argList << pages(printer, useCupsOptions, version);
0872     // }
0873 
0874     if (useCupsOptions && !cupsOptions(printer, documentOrientation).isEmpty()) {
0875         argList << cupsOptions(printer, documentOrientation);
0876     }
0877 
0878     if (version == QLatin1String("lp")) {
0879         argList << QStringLiteral("--");
0880     }
0881 
0882     return argList;
0883 }
0884 
0885 bool PrintPortal::cupsAvailable()
0886 {
0887     // Ideally we would have access to the private Qt method
0888     // QCUPSSupport::cupsAvailable() to do this as it is very complex routine.
0889     // However, if CUPS is available then QPrinter::numCopies() will always return 1
0890     // whereas if CUPS is not available it will return the real number of copies.
0891     // This behaviour is guaranteed never to change, so we can use it as a reliable substitute.
0892     QPrinter testPrinter;
0893     return testPrinter.supportsMultipleCopies();
0894 }