File indexing completed on 2024-05-12 09:41:58
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 uint PrintPortal::Print(const QDBusObjectPath &handle, 0209 const QString &app_id, 0210 const QString &parent_window, 0211 const QString &title, 0212 const QDBusUnixFileDescriptor &fd, 0213 const QVariantMap &options, 0214 QVariantMap &results) 0215 { 0216 Q_UNUSED(results) 0217 qCDebug(XdgDesktopPortalKdePrint) << "Print called with parameters:"; 0218 qCDebug(XdgDesktopPortalKdePrint) << " handle: " << handle.path(); 0219 qCDebug(XdgDesktopPortalKdePrint) << " app_id: " << app_id; 0220 qCDebug(XdgDesktopPortalKdePrint) << " parent_window: " << parent_window; 0221 qCDebug(XdgDesktopPortalKdePrint) << " title: " << title; 0222 qCDebug(XdgDesktopPortalKdePrint) << " fd: " << fd.fileDescriptor(); 0223 qCDebug(XdgDesktopPortalKdePrint) << " options: " << options; 0224 0225 QFile fileToPrint; 0226 if (fileToPrint.open(fd.fileDescriptor(), QIODevice::ReadOnly)) { 0227 QPrinter *printer = nullptr; 0228 // Use printer associated with token if possible 0229 if (options.contains(QStringLiteral("token"))) { 0230 printer = m_printers.value(options.value(QStringLiteral("token")).toUInt()); 0231 } else { 0232 // Use the last configured printer otherwise 0233 if (m_printers.count()) { 0234 printer = m_printers.last(); 0235 } 0236 } 0237 0238 if (!printer) { 0239 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: no QPrinter what can be used for printing"; 0240 return 1; 0241 } 0242 0243 // We are going to print to a file 0244 if (!printer->outputFileName().isEmpty()) { 0245 if (QFile::exists(printer->outputFileName())) { 0246 QFile::remove(printer->outputFileName()); 0247 } 0248 0249 QByteArray pdfContent = fileToPrint.readAll(); 0250 QFile outputFile(printer->outputFileName()); 0251 if (outputFile.open(QIODevice::ReadWrite)) { 0252 outputFile.write(pdfContent); 0253 outputFile.close(); 0254 } else { 0255 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: couldn't open the file for writing"; 0256 } 0257 0258 fileToPrint.close(); 0259 0260 return 0; 0261 0262 // TODO poscript support? 0263 0264 // Print to a printer via lpr command 0265 } else { 0266 // The code below is copied from Okular 0267 bool useCupsOptions = cupsAvailable(); 0268 QString exe; 0269 QStringList argList; 0270 0271 // Decide what executable to use to print with, need the CUPS version of lpr if available 0272 // Some distros name the CUPS version of lpr as lpr-cups or lpr.cups so try those first 0273 // before default to lpr, or failing that to lp 0274 if (!QStandardPaths::findExecutable(QStringLiteral("lpr-cups")).isEmpty()) { 0275 exe = QStringLiteral("lpr-cups"); 0276 } else if (!QStandardPaths::findExecutable(QStringLiteral("lpr.cups")).isEmpty()) { 0277 exe = QStringLiteral("lpr.cups"); 0278 } else if (!QStandardPaths::findExecutable(QStringLiteral("lpr")).isEmpty()) { 0279 exe = QStringLiteral("lpr"); 0280 } else if (!QStandardPaths::findExecutable(QStringLiteral("lp")).isEmpty()) { 0281 exe = QStringLiteral("lp"); 0282 } else { 0283 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: couldn't run lpr command for printing"; 0284 return 1; 0285 } 0286 0287 QTemporaryFile tempFile; 0288 if (tempFile.open()) { 0289 tempFile.write(fileToPrint.readAll()); 0290 tempFile.close(); 0291 } else { 0292 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: couldn't create temporary file for printing"; 0293 return 1; 0294 } 0295 0296 argList = printArguments(printer, useCupsOptions, exe, printer->pageLayout().orientation()) << tempFile.fileName(); 0297 // qCDebug(XdgDesktopPortalKdePrint) << "Executing" << exe << "with arguments" << argList << tempFile.fileName(); 0298 int retValue = KProcess::execute(exe, argList); 0299 0300 if (retValue <= 0) { 0301 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: running KProcess failed"; 0302 return 1; 0303 } 0304 0305 return retValue; 0306 } 0307 } else { 0308 qCDebug(XdgDesktopPortalKdePrint) << "Failed to print: couldn't not read from fd"; 0309 return 1; 0310 } 0311 0312 return 0; 0313 } 0314 0315 uint PrintPortal::PreparePrint(const QDBusObjectPath &handle, 0316 const QString &app_id, 0317 const QString &parent_window, 0318 const QString &title, 0319 const QVariantMap &settings, 0320 const QVariantMap &page_setup, 0321 const QVariantMap &options, 0322 QVariantMap &results) 0323 { 0324 qCDebug(XdgDesktopPortalKdePrint) << "PreparePrint called with parameters:"; 0325 qCDebug(XdgDesktopPortalKdePrint) << " handle: " << handle.path(); 0326 qCDebug(XdgDesktopPortalKdePrint) << " app_id: " << app_id; 0327 qCDebug(XdgDesktopPortalKdePrint) << " parent_window: " << parent_window; 0328 qCDebug(XdgDesktopPortalKdePrint) << " title: " << title; 0329 qCDebug(XdgDesktopPortalKdePrint) << " settings: " << settings; 0330 qCDebug(XdgDesktopPortalKdePrint) << " page_setup: " << page_setup; 0331 qCDebug(XdgDesktopPortalKdePrint) << " options: " << options; 0332 0333 // Create new one 0334 QPrinter *printer = new QPrinter(); 0335 0336 // First we have to load pre-configured options 0337 0338 // Process settings (used by printer) 0339 QCUPSSupport::PagesPerSheet pagesPerSheet = QCUPSSupport::OnePagePerSheet; 0340 QCUPSSupport::PagesPerSheetLayout pagesPerSheetLayout = QCUPSSupport::LeftToRightTopToBottom; 0341 for (auto it = settings.constBegin(); it != settings.constEnd(); ++it) { 0342 if (it.key() == QStringLiteral("orientation")) { 0343 const QString orientation = it.value().toString(); 0344 if (orientation == QLatin1String("landscape") || orientation == QLatin1String("reverse_landscape")) { 0345 printer->setPageOrientation(QPageLayout::Landscape); 0346 } else if (orientation == QLatin1String("portrait") || orientation == QLatin1String("reverse_portrait")) { 0347 printer->setPageOrientation(QPageLayout::Portrait); 0348 } 0349 } else if (it.key() == QStringLiteral("n-copies")) { 0350 printer->setCopyCount(it.value().toString().toInt()); 0351 } else if (it.key() == QStringLiteral("resolution")) { 0352 printer->setResolution(it.value().toString().toInt()); 0353 } else if (it.key() == QStringLiteral("use-color")) { 0354 printer->setColorMode(it.value().toString() == QLatin1String("yes") ? QPrinter::Color : QPrinter::GrayScale); 0355 } else if (it.key() == QStringLiteral("duplex")) { 0356 const QString duplex = it.value().toString(); 0357 if (duplex == QLatin1String("simplex")) { 0358 printer->setDuplex(QPrinter::DuplexNone); 0359 } else if (duplex == QLatin1String("horizontal")) { 0360 printer->setDuplex(QPrinter::DuplexShortSide); 0361 } else if (duplex == QLatin1String("vertical")) { 0362 printer->setDuplex(QPrinter::DuplexLongSide); 0363 } 0364 } else if (it.key() == QStringLiteral("collate")) { 0365 printer->setCollateCopies(it.value().toString() == QLatin1String("yes")); 0366 } else if (it.key() == QStringLiteral("reverse")) { 0367 printer->setPageOrder(it.value().toString() == QLatin1String("yes") ? QPrinter::LastPageFirst : QPrinter::FirstPageFirst); 0368 } else if (it.key() == QStringLiteral("print-pages")) { 0369 const QString printPages = it.value().toString(); 0370 if (printPages == QLatin1String("all")) { 0371 printer->setPrintRange(QPrinter::AllPages); 0372 } else if (printPages == QLatin1String("selection")) { 0373 printer->setPrintRange(QPrinter::Selection); 0374 } else if (printPages == QLatin1String("current")) { 0375 printer->setPrintRange(QPrinter::CurrentPage); 0376 } else if (printPages == QLatin1String("ranges")) { 0377 printer->setPrintRange(QPrinter::PageRange); 0378 } 0379 } else if (it.key() == QStringLiteral("page-ranges")) { 0380 const QString range = it.value().toString(); 0381 // Gnome supports format like 1-5,7,9,11-15, however Qt support only e.g.1-15 0382 // so get the first and the last value 0383 const QStringList ranges = range.split(QLatin1Char(',')); 0384 if (ranges.count()) { 0385 QStringList firstRangeValues = ranges.first().split(QLatin1Char('-')); 0386 QStringList lastRangeValues = ranges.last().split(QLatin1Char('-')); 0387 printer->setFromTo(firstRangeValues.first().toInt(), lastRangeValues.last().toInt()); 0388 } 0389 } else if (it.key() == QStringLiteral("page-set")) { 0390 // WARNING Qt internal private API, anyway the print dialog doesn't seem to 0391 // read these properties, but I'll leave it here in case this changes in future 0392 const QString pageSet = it.value().toString(); 0393 if (pageSet == QLatin1String("all")) { 0394 QCUPSSupport::setPageSet(printer, QCUPSSupport::AllPages); 0395 } else if (pageSet == QLatin1String("even")) { 0396 QCUPSSupport::setPageSet(printer, QCUPSSupport::EvenPages); 0397 } else if (pageSet == QLatin1String("odd")) { 0398 QCUPSSupport::setPageSet(printer, QCUPSSupport::OddPages); 0399 } 0400 } else if (it.key() == QStringLiteral("number-up")) { 0401 // WARNING Qt internal private API, anyway the print dialog doesn't seem to 0402 // read these properties, but I'll leave it here in case this changes in future 0403 const QString numberUp = it.value().toString(); 0404 if (numberUp == QLatin1String("1")) { 0405 pagesPerSheet = QCUPSSupport::OnePagePerSheet; 0406 } else if (numberUp == QLatin1String("2")) { 0407 pagesPerSheet = QCUPSSupport::TwoPagesPerSheet; 0408 } else if (numberUp == QLatin1String("4")) { 0409 pagesPerSheet = QCUPSSupport::FourPagesPerSheet; 0410 } else if (numberUp == QLatin1String("6")) { 0411 pagesPerSheet = QCUPSSupport::SixPagesPerSheet; 0412 } else if (numberUp == QLatin1String("9")) { 0413 pagesPerSheet = QCUPSSupport::NinePagesPerSheet; 0414 } else if (numberUp == QLatin1String("16")) { 0415 pagesPerSheet = QCUPSSupport::SixteenPagesPerSheet; 0416 } 0417 } else if (it.key() == QStringLiteral("number-up-layout")) { 0418 // WARNING Qt internal private API, anyway the print dialog doesn't seem to 0419 // read these properties, but I'll leave it here in case this changes in future 0420 const QString layout = it.value().toString(); 0421 if (layout == QLatin1String("lrtb")) { 0422 pagesPerSheetLayout = QCUPSSupport::LeftToRightTopToBottom; 0423 } else if (layout == QLatin1String("lrbt")) { 0424 pagesPerSheetLayout = QCUPSSupport::LeftToRightBottomToTop; 0425 } else if (layout == QLatin1String("rltb")) { 0426 pagesPerSheetLayout = QCUPSSupport::RightToLeftTopToBottom; 0427 } else if (layout == QLatin1String("rlbt")) { 0428 pagesPerSheetLayout = QCUPSSupport::RightToLeftBottomToTop; 0429 } else if (layout == QLatin1String("tblr")) { 0430 pagesPerSheetLayout = QCUPSSupport::TopToBottomLeftToRight; 0431 } else if (layout == QLatin1String("tbrl")) { 0432 pagesPerSheetLayout = QCUPSSupport::TopToBottomRightToLeft; 0433 } else if (layout == QLatin1String("btlr")) { 0434 pagesPerSheetLayout = QCUPSSupport::BottomToTopLeftToRight; 0435 } else if (layout == QLatin1String("btrl")) { 0436 pagesPerSheetLayout = QCUPSSupport::BottomToTopRightToLeft; 0437 } 0438 } else if (it.key() == QStringLiteral("output-file-format")) { 0439 // TODO only PDF supported by Qt so when printing to file we can use PDF only 0440 // btw. this should be already set automatically because we set output file name 0441 printer->setOutputFormat(QPrinter::PdfFormat); 0442 } else if (it.key() == QStringLiteral("output-uri")) { 0443 const QUrl uri = QUrl(it.value().toString()); 0444 // Check whether the uri is not just a directory name and whether we don't need to 0445 // append output-basename 0446 if (settings.contains(QStringLiteral("output-basename"))) { 0447 const QString basename = settings.value(QStringLiteral("output-basename")).toString(); 0448 if (!uri.toDisplayString().endsWith(basename) && uri.toDisplayString().endsWith(QLatin1Char('/'))) { 0449 printer->setOutputFileName(uri.toDisplayString() + basename); 0450 } 0451 } else { 0452 printer->setOutputFileName(uri.toDisplayString()); 0453 } 0454 } else { 0455 qCDebug(XdgDesktopPortalKdePrint) << "Unhandled printer setting: " << it.key(); 0456 } 0457 } 0458 0459 QCUPSSupport::setPagesPerSheetLayout(printer, pagesPerSheet, pagesPerSheetLayout); 0460 0461 // Process page setup 0462 QSizeF paperSize; 0463 QString ppdName; 0464 QString name; 0465 QMarginsF pageMargins = printer->pageLayout().margins(QPageLayout::Millimeter); 0466 for (auto it = page_setup.constBegin(); it != page_setup.constEnd(); ++it) { 0467 if (it.key() == QStringLiteral("PPDName")) { 0468 ppdName = it.value().toString(); 0469 } else if (it.key() == QStringLiteral("Name")) { 0470 name = it.value().toString(); 0471 } 0472 0473 if (it.key() == QStringLiteral("Width")) { 0474 paperSize.setWidth(it.value().toReal()); 0475 } else if (it.key() == QStringLiteral("Height")) { 0476 paperSize.setHeight(it.value().toReal()); 0477 } else if (it.key() == QStringLiteral("MarginTop")) { 0478 pageMargins.setTop(it.value().toReal()); 0479 } else if (it.key() == QStringLiteral("MarginBottom")) { 0480 pageMargins.setBottom(it.value().toReal()); 0481 } else if (it.key() == QStringLiteral("MarginLeft")) { 0482 pageMargins.setLeft(it.value().toReal()); 0483 } else if (it.key() == QStringLiteral("MarginRight")) { 0484 pageMargins.setRight(it.value().toReal()); 0485 } else if (it.key() == QStringLiteral("Orientation")) { 0486 const QString orientation = it.value().toString(); 0487 if (orientation == QLatin1String("landscape") || orientation == QLatin1String("reverse_landscape")) { 0488 printer->setPageOrientation(QPageLayout::Landscape); 0489 } else if (orientation == QLatin1String("portrait") || orientation == QLatin1String("reverse_portrait")) { 0490 printer->setPageOrientation(QPageLayout::Portrait); 0491 } 0492 } else { 0493 qCDebug(XdgDesktopPortalKdePrint) << "Unhandled page setup: " << it.key(); 0494 } 0495 } 0496 0497 if (!ppdName.isEmpty()) { 0498 printer->setPageSize(QPageSize(qt_idForPpdKey(ppdName))); 0499 } else if (!name.isEmpty()) { 0500 printer->setPageSize(QPageSize(paperSize, QPageSize::Millimeter, name)); 0501 } 0502 printer->setPageMargins(pageMargins, QPageLayout::Millimeter); 0503 0504 QPrintDialog *printDialog = new QPrintDialog(printer); 0505 Utils::setParentWindow(printDialog, parent_window); 0506 Request::makeClosableDialogRequest(handle, printDialog); 0507 0508 // Process options 0509 if (options.contains(QStringLiteral("modal"))) { 0510 printDialog->setModal(options.value(QStringLiteral("modal")).toBool()); 0511 } 0512 0513 // Pass back what we configured 0514 if (printDialog->exec() == QDialog::Accepted) { 0515 QVariantMap resultingSettings; 0516 QVariantMap resultingPageSetup; 0517 0518 // Process back printer settings 0519 resultingSettings.insert(QStringLiteral("n-copies"), QString::number(printer->copyCount())); 0520 resultingSettings.insert(QStringLiteral("resolution"), QString::number(printer->resolution())); 0521 resultingSettings.insert(QStringLiteral("use-color"), printer->colorMode() == QPrinter::Color ? QLatin1String("yes") : QLatin1String("no")); 0522 if (printer->duplex() == QPrinter::DuplexNone) { 0523 resultingSettings.insert(QStringLiteral("duplex"), QLatin1String("simplex")); 0524 } else if (printer->duplex() == QPrinter::DuplexShortSide) { 0525 resultingSettings.insert(QStringLiteral("duplex"), QLatin1String("horizontal")); 0526 } else if (printer->duplex() == QPrinter::DuplexLongSide) { 0527 resultingSettings.insert(QStringLiteral("duplex"), QLatin1String("vertical")); 0528 } 0529 resultingSettings.insert(QStringLiteral("collate"), printer->collateCopies() ? QLatin1String("yes") : QLatin1String("no")); 0530 resultingSettings.insert(QStringLiteral("reverse"), printer->pageOrder() == QPrinter::LastPageFirst ? QLatin1String("yes") : QLatin1String("no")); 0531 if (printer->printRange() == QPrinter::AllPages) { 0532 resultingSettings.insert(QStringLiteral("print-pages"), QLatin1String("all")); 0533 } else if (printer->printRange() == QPrinter::Selection) { 0534 resultingSettings.insert(QStringLiteral("print-pages"), QLatin1String("selection")); 0535 } else if (printer->printRange() == QPrinter::CurrentPage) { 0536 resultingSettings.insert(QStringLiteral("print-pages"), QLatin1String("current")); 0537 } else if (printer->printRange() == QPrinter::PageRange) { 0538 resultingSettings.insert(QStringLiteral("print-pages"), QLatin1String("ranges")); 0539 const int fromPageToIndex = printer->fromPage() ? printer->fromPage() - 1 : printer->fromPage(); 0540 const int toPageToIndex = printer->toPage() ? printer->toPage() - 1 : printer->toPage(); 0541 resultingSettings.insert(QStringLiteral("page-ranges"), QStringLiteral("%1-%2").arg(fromPageToIndex).arg(toPageToIndex)); 0542 } 0543 // Set cups specific properties 0544 const QStringList cupsOptions = printer->printEngine()->property(PPK_CupsOptions).toStringList(); 0545 qCDebug(XdgDesktopPortalKdePrint) << cupsOptions; 0546 if (cupsOptions.contains(QLatin1String("page-set"))) { 0547 resultingSettings.insert(QStringLiteral("page-set"), cupsOptions.at(cupsOptions.indexOf(QStringLiteral("page-set")) + 1)); 0548 } 0549 if (cupsOptions.contains(QLatin1String("number-up"))) { 0550 resultingSettings.insert(QStringLiteral("number-up"), cupsOptions.at(cupsOptions.indexOf(QStringLiteral("number-up")) + 1)); 0551 } 0552 if (cupsOptions.contains(QLatin1String("number-up-layout"))) { 0553 resultingSettings.insert(QStringLiteral("number-up-layout"), cupsOptions.at(cupsOptions.indexOf(QStringLiteral("number-up-layout")) + 1)); 0554 } 0555 0556 if (printer->outputFormat() == QPrinter::PdfFormat) { 0557 resultingSettings.insert(QStringLiteral("output-file-format"), QLatin1String("pdf")); 0558 } 0559 0560 if (!printer->outputFileName().isEmpty()) { 0561 resultingSettings.insert(QStringLiteral("output-uri"), QUrl::fromLocalFile(printer->outputFileName()).toDisplayString()); 0562 } 0563 0564 // Process back page setup 0565 if (printer->pageLayout().pageSize().id() == QPageSize::Custom) { 0566 resultingPageSetup.insert(QStringLiteral("Name"), printer->pageLayout().pageSize().name()); 0567 resultingPageSetup.insert(QStringLiteral("DisplayName"), printer->pageLayout().pageSize().name()); 0568 } else { 0569 resultingPageSetup.insert(QStringLiteral("PPDName"), qt_keyForPageSizeId(printer->pageLayout().pageSize().id())); 0570 resultingPageSetup.insert(QStringLiteral("DisplayName"), qt_keyForPageSizeId(printer->pageLayout().pageSize().id())); 0571 } 0572 resultingPageSetup.insert(QStringLiteral("Width"), printer->pageLayout().pageSize().size(QPageSize::Millimeter).width()); 0573 resultingPageSetup.insert(QStringLiteral("Height"), printer->pageLayout().pageSize().size(QPageSize::Millimeter).height()); 0574 resultingPageSetup.insert(QStringLiteral("MarginTop"), printer->pageLayout().margins(QPageLayout::Millimeter).top()); 0575 resultingPageSetup.insert(QStringLiteral("MarginBottom"), printer->pageLayout().margins(QPageLayout::Millimeter).bottom()); 0576 resultingPageSetup.insert(QStringLiteral("MarginLeft"), printer->pageLayout().margins(QPageLayout::Millimeter).left()); 0577 resultingPageSetup.insert(QStringLiteral("MarginRight"), printer->pageLayout().margins(QPageLayout::Millimeter).right()); 0578 resultingPageSetup.insert(QStringLiteral("Orientation"), 0579 printer->pageLayout().orientation() == QPageLayout::Landscape ? QLatin1String("landscape") : QLatin1String("portrait")); 0580 0581 qCDebug(XdgDesktopPortalKdePrint) << "Settings: "; 0582 qCDebug(XdgDesktopPortalKdePrint) << "---------------------------"; 0583 qCDebug(XdgDesktopPortalKdePrint) << resultingSettings; 0584 qCDebug(XdgDesktopPortalKdePrint) << "Page setup: "; 0585 qCDebug(XdgDesktopPortalKdePrint) << "---------------------------"; 0586 qCDebug(XdgDesktopPortalKdePrint) << resultingPageSetup; 0587 0588 uint token = QDateTime::currentDateTime().toSecsSinceEpoch(); 0589 results.insert(QStringLiteral("settings"), resultingSettings); 0590 results.insert(QStringLiteral("page-setup"), resultingPageSetup); 0591 results.insert(QStringLiteral("token"), token); 0592 0593 m_printers.insert(token, printer); 0594 0595 printDialog->deleteLater(); 0596 return 0; 0597 } else { 0598 printDialog->deleteLater(); 0599 return 1; 0600 } 0601 0602 return 0; 0603 } 0604 0605 QStringList PrintPortal::destination(const QPrinter *printer, const QString &version) 0606 { 0607 if (version == QLatin1String("lp")) { 0608 return QStringList(QStringLiteral("-d")) << printer->printerName(); 0609 } 0610 0611 if (version.startsWith(QLatin1String("lpr"))) { 0612 return QStringList(QStringLiteral("-P")) << printer->printerName(); 0613 } 0614 0615 return QStringList(); 0616 } 0617 0618 QStringList PrintPortal::copies(const QPrinter *printer, const QString &version) 0619 { 0620 int cp = printer->copyCount(); 0621 0622 if (version == QLatin1String("lp")) { 0623 return QStringList(QStringLiteral("-n")) << QStringLiteral("%1").arg(cp); 0624 } 0625 0626 if (version.startsWith(QLatin1String("lpr"))) { 0627 return QStringList() << QStringLiteral("-#%1").arg(cp); 0628 } 0629 0630 return QStringList(); 0631 } 0632 0633 QStringList PrintPortal::jobname(const QPrinter *printer, const QString &version) 0634 { 0635 if (!printer->docName().isEmpty()) { 0636 if (version == QLatin1String("lp")) { 0637 return QStringList(QStringLiteral("-t")) << printer->docName(); 0638 } 0639 0640 if (version.startsWith(QLatin1String("lpr"))) { 0641 const QString shortenedDocName = QString::fromUtf8(printer->docName().toUtf8().left(255)); 0642 return QStringList(QStringLiteral("-J")) << shortenedDocName; 0643 } 0644 } 0645 0646 return QStringList(); 0647 } 0648 0649 // What about Upper and MultiPurpose? And others in PPD??? 0650 QString PrintPortal::mediaPaperSource(const QPrinter *printer) 0651 { 0652 switch (printer->paperSource()) { 0653 case QPrinter::Auto: 0654 return QString(); 0655 case QPrinter::Cassette: 0656 return QStringLiteral("Cassette"); 0657 case QPrinter::Envelope: 0658 return QStringLiteral("Envelope"); 0659 case QPrinter::EnvelopeManual: 0660 return QStringLiteral("EnvelopeManual"); 0661 case QPrinter::FormSource: 0662 return QStringLiteral("FormSource"); 0663 case QPrinter::LargeCapacity: 0664 return QStringLiteral("LargeCapacity"); 0665 case QPrinter::LargeFormat: 0666 return QStringLiteral("LargeFormat"); 0667 case QPrinter::Lower: 0668 return QStringLiteral("Lower"); 0669 case QPrinter::MaxPageSource: 0670 return QStringLiteral("MaxPageSource"); 0671 case QPrinter::Middle: 0672 return QStringLiteral("Middle"); 0673 case QPrinter::Manual: 0674 return QStringLiteral("Manual"); 0675 case QPrinter::OnlyOne: 0676 return QStringLiteral("OnlyOne"); 0677 case QPrinter::Tractor: 0678 return QStringLiteral("Tractor"); 0679 case QPrinter::SmallFormat: 0680 return QStringLiteral("SmallFormat"); 0681 default: 0682 return QString(); 0683 } 0684 } 0685 0686 QStringList PrintPortal::optionOrientation(const QPrinter *printer, QPageLayout::Orientation documentOrientation) 0687 { 0688 // portrait and landscape options rotate the document according to the document orientation 0689 // If we want to print a landscape document as one would expect it, we have to pass the 0690 // portrait option so that the document is not rotated additionally 0691 if (printer->pageLayout().orientation() == documentOrientation) { 0692 // the user wants the document printed as is 0693 return QStringList(QStringLiteral("-o")) << QStringLiteral("portrait"); 0694 } else { 0695 // the user expects the document being rotated by 90 degrees 0696 return QStringList(QStringLiteral("-o")) << QStringLiteral("landscape"); 0697 } 0698 } 0699 0700 QStringList PrintPortal::optionDoubleSidedPrinting(const QPrinter *printer) 0701 { 0702 switch (printer->duplex()) { 0703 case QPrinter::DuplexNone: 0704 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=one-sided"); 0705 case QPrinter::DuplexAuto: 0706 if (printer->pageLayout().orientation() == QPageLayout::Landscape) { 0707 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-short-edge"); 0708 } else { 0709 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-long-edge"); 0710 } 0711 case QPrinter::DuplexLongSide: 0712 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-long-edge"); 0713 case QPrinter::DuplexShortSide: 0714 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-short-edge"); 0715 default: 0716 return QStringList(); // Use printer default 0717 } 0718 } 0719 0720 QStringList PrintPortal::optionPageOrder(const QPrinter *printer) 0721 { 0722 if (printer->pageOrder() == QPrinter::LastPageFirst) { 0723 return QStringList(QStringLiteral("-o")) << QStringLiteral("outputorder=reverse"); 0724 } 0725 return QStringList(QStringLiteral("-o")) << QStringLiteral("outputorder=normal"); 0726 } 0727 0728 QStringList PrintPortal::optionCollateCopies(const QPrinter *printer) 0729 { 0730 if (printer->collateCopies()) { 0731 return QStringList(QStringLiteral("-o")) << QStringLiteral("Collate=True"); 0732 } 0733 return QStringList(QStringLiteral("-o")) << QStringLiteral("Collate=False"); 0734 } 0735 0736 QStringList PrintPortal::optionPageMargins(const QPrinter *printer) 0737 { 0738 if (printer->printEngine()->property(QPrintEngine::PPK_PageMargins).isNull()) { 0739 return QStringList(); 0740 } else { 0741 qreal l, t, r, b; 0742 QMarginsF m = printer->pageLayout().margins(); 0743 l = m.left(); 0744 t = m.top(); 0745 r = m.right(); 0746 b = m.bottom(); 0747 return QStringList{ 0748 QStringLiteral("-o"), 0749 QStringLiteral("page-left=%1").arg(l), 0750 QStringLiteral("-o"), 0751 QStringLiteral("page-top=%1").arg(t), 0752 QStringLiteral("-o"), 0753 QStringLiteral("page-right=%1").arg(r), 0754 QStringLiteral("-o"), 0755 QStringLiteral("page-bottom=%1").arg(b), 0756 QStringLiteral("-o"), 0757 QStringLiteral("fit-to-page"), 0758 }; 0759 } 0760 } 0761 0762 QStringList PrintPortal::optionCupsProperties(const QPrinter *printer) 0763 { 0764 QStringList dialogOptions = printer->printEngine()->property(QPrintEngine::PrintEnginePropertyKey(0xfe00)).toStringList(); 0765 QStringList cupsOptions; 0766 0767 for (int i = 0; i < dialogOptions.count(); i = i + 2) { 0768 // Ignore some cups properties as the pdf we get is already formatted using these 0769 if (dialogOptions[i] == QLatin1String("number-up") || dialogOptions[i] == QLatin1String("number-up-layout")) { 0770 continue; 0771 } 0772 0773 if (dialogOptions[i + 1].isEmpty()) { 0774 cupsOptions << QStringLiteral("-o") << dialogOptions[i]; 0775 } else { 0776 cupsOptions << QStringLiteral("-o") << dialogOptions[i] + QLatin1Char('=') + dialogOptions[i + 1]; 0777 } 0778 } 0779 0780 return cupsOptions; 0781 } 0782 0783 QStringList PrintPortal::optionMedia(const QPrinter *printer) 0784 { 0785 if (!qt_keyForPageSizeId(printer->pageLayout().pageSize().id()).isEmpty() && !mediaPaperSource(printer).isEmpty()) { 0786 return QStringList(QStringLiteral("-o")) 0787 << QStringLiteral("media=%1,%2").arg(qt_keyForPageSizeId(printer->pageLayout().pageSize().id()), mediaPaperSource(printer)); 0788 } 0789 0790 if (!qt_keyForPageSizeId(printer->pageLayout().pageSize().id()).isEmpty()) { 0791 return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1").arg(qt_keyForPageSizeId(printer->pageLayout().pageSize().id())); 0792 } 0793 0794 if (!mediaPaperSource(printer).isEmpty()) { 0795 return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1").arg(mediaPaperSource(printer)); 0796 } 0797 0798 return QStringList(); 0799 } 0800 0801 QStringList PrintPortal::pages(const QPrinter *printer, bool useCupsOptions, const QString &version) 0802 { 0803 if (printer->printRange() == QPrinter::PageRange) { 0804 if (version == QLatin1String("lp")) { 0805 return QStringList(QStringLiteral("-P")) << QStringLiteral("%1-%2").arg(printer->fromPage()).arg(printer->toPage()); 0806 } 0807 0808 if (version.startsWith(QLatin1String("lpr")) && useCupsOptions) { 0809 return QStringList(QStringLiteral("-o")) << QStringLiteral("page-ranges=%1-%2").arg(printer->fromPage()).arg(printer->toPage()); 0810 } 0811 } 0812 0813 return QStringList(); // AllPages 0814 } 0815 0816 QStringList PrintPortal::cupsOptions(const QPrinter *printer, QPageLayout::Orientation documentOrientation) 0817 { 0818 Q_UNUSED(documentOrientation) 0819 QStringList optionList; 0820 0821 // if (!optionMedia(printer).isEmpty()) { 0822 // optionList << optionMedia(printer); 0823 // } 0824 0825 // if (!optionOrientation(printer, documentOrientation).isEmpty()) { 0826 // optionList << optionOrientation(printer, documentOrientation); 0827 // } 0828 0829 if (!optionDoubleSidedPrinting(printer).isEmpty()) { 0830 optionList << optionDoubleSidedPrinting(printer); 0831 } 0832 0833 if (!optionPageOrder(printer).isEmpty()) { 0834 optionList << optionPageOrder(printer); 0835 } 0836 0837 if (!optionCollateCopies(printer).isEmpty()) { 0838 optionList << optionCollateCopies(printer); 0839 } 0840 0841 // if (!optionPageMargins(printer).isEmpty()) { 0842 // optionList << optionPageMargins(printer); 0843 // } 0844 0845 optionList << optionCupsProperties(printer); 0846 0847 return optionList; 0848 } 0849 0850 QStringList PrintPortal::printArguments(const QPrinter *printer, bool useCupsOptions, const QString &version, QPageLayout::Orientation documentOrientation) 0851 { 0852 QStringList argList; 0853 0854 if (!destination(printer, version).isEmpty()) { 0855 argList << destination(printer, version); 0856 } 0857 0858 if (!copies(printer, version).isEmpty()) { 0859 argList << copies(printer, version); 0860 } 0861 0862 if (!jobname(printer, version).isEmpty()) { 0863 argList << jobname(printer, version); 0864 } 0865 0866 // if (!pages(printer, useCupsOptions, version).isEmpty()) { 0867 // argList << pages(printer, useCupsOptions, version); 0868 // } 0869 0870 if (useCupsOptions && !cupsOptions(printer, documentOrientation).isEmpty()) { 0871 argList << cupsOptions(printer, documentOrientation); 0872 } 0873 0874 if (version == QLatin1String("lp")) { 0875 argList << QStringLiteral("--"); 0876 } 0877 0878 return argList; 0879 } 0880 0881 bool PrintPortal::cupsAvailable() 0882 { 0883 // Ideally we would have access to the private Qt method 0884 // QCUPSSupport::cupsAvailable() to do this as it is very complex routine. 0885 // However, if CUPS is available then QPrinter::numCopies() will always return 1 0886 // whereas if CUPS is not available it will return the real number of copies. 0887 // This behaviour is guaranteed never to change, so we can use it as a reliable substitute. 0888 QPrinter testPrinter; 0889 return testPrinter.supportsMultipleCopies(); 0890 }