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 }