File indexing completed on 2024-04-28 04:32:42
0001 /* 0002 SPDX-FileCopyrightText: 2007, 2010 John Layt <john@layt.net> 0003 0004 FilePrinterPreview based on KPrintPreview (originally LGPL) 0005 SPDX-FileCopyrightText: 2007 Alex Merry <huntedhacker@tiscali.co.uk> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "fileprinter.h" 0011 0012 #include <QFile> 0013 #include <QFileInfo> 0014 #include <QLabel> 0015 #include <QPrintEngine> 0016 #include <QShowEvent> 0017 #include <QSize> 0018 #include <QStringList> 0019 #include <QTcpSocket> 0020 0021 #include <KProcess> 0022 #include <KShell> 0023 #include <QDebug> 0024 #include <QStandardPaths> 0025 0026 #include "debug_p.h" 0027 0028 using namespace Okular; 0029 0030 Document::PrintError 0031 FilePrinter::printFile(QPrinter &printer, const QString &file, QPageLayout::Orientation documentOrientation, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, ScaleMode scaleMode) 0032 { 0033 FilePrinter fp; 0034 return fp.doPrintFiles(printer, QStringList(file), fileDeletePolicy, pageSelectPolicy, pageRange, documentOrientation, scaleMode); 0035 } 0036 0037 static Document::PrintError doKProcessExecute(const QString &exe, const QStringList &argList) 0038 { 0039 const int ret = KProcess::execute(exe, argList); 0040 if (ret == -1) { 0041 return Document::PrintingProcessCrashPrintError; 0042 } 0043 if (ret == -2) { 0044 return Document::PrintingProcessStartPrintError; 0045 } 0046 if (ret < 0) { 0047 return Document::UnknownPrintError; 0048 } 0049 0050 return Document::NoPrintError; 0051 } 0052 0053 Document::PrintError 0054 FilePrinter::doPrintFiles(QPrinter &printer, const QStringList &fileList, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, QPageLayout::Orientation documentOrientation, ScaleMode scaleMode) 0055 { 0056 if (fileList.size() < 1) { 0057 return Document::NoFileToPrintError; 0058 } 0059 0060 for (QStringList::ConstIterator it = fileList.constBegin(); it != fileList.constEnd(); ++it) { 0061 if (!QFile::exists(*it)) { 0062 return Document::UnableToFindFilePrintError; 0063 } 0064 } 0065 0066 if (printer.printerState() == QPrinter::Aborted || printer.printerState() == QPrinter::Error) { 0067 return Document::InvalidPrinterStatePrintError; 0068 } 0069 0070 QString exe; 0071 QStringList argList; 0072 Document::PrintError ret; 0073 0074 // Print to File if a filename set, assumes there must be only 1 file 0075 if (!printer.outputFileName().isEmpty()) { 0076 if (QFile::exists(printer.outputFileName())) { 0077 QFile::remove(printer.outputFileName()); 0078 } 0079 0080 QFileInfo inputFileInfo = QFileInfo(fileList[0]); 0081 QFileInfo outputFileInfo = QFileInfo(printer.outputFileName()); 0082 0083 bool doDeleteFile = (fileDeletePolicy == FilePrinter::SystemDeletesFiles); 0084 if (inputFileInfo.suffix() == outputFileInfo.suffix()) { 0085 if (doDeleteFile) { 0086 bool res = QFile::rename(fileList[0], printer.outputFileName()); 0087 if (res) { 0088 doDeleteFile = false; 0089 ret = Document::NoPrintError; 0090 } else { 0091 ret = Document::PrintToFilePrintError; 0092 } 0093 } else { 0094 bool res = QFile::copy(fileList[0], printer.outputFileName()); 0095 if (res) { 0096 ret = Document::NoPrintError; 0097 } else { 0098 ret = Document::PrintToFilePrintError; 0099 } 0100 } 0101 } else if (inputFileInfo.suffix() == QLatin1String("ps") && printer.outputFormat() == QPrinter::PdfFormat && ps2pdfAvailable()) { 0102 exe = QStringLiteral("ps2pdf"); 0103 argList << fileList[0] << printer.outputFileName(); 0104 qCDebug(OkularCoreDebug) << "Executing" << exe << "with arguments" << argList; 0105 ret = doKProcessExecute(exe, argList); 0106 } else if (inputFileInfo.suffix() == QLatin1String("pdf") && printer.outputFormat() == QPrinter::NativeFormat && pdf2psAvailable()) { 0107 exe = QStringLiteral("pdf2ps"); 0108 argList << fileList[0] << printer.outputFileName(); 0109 qCDebug(OkularCoreDebug) << "Executing" << exe << "with arguments" << argList; 0110 ret = doKProcessExecute(exe, argList); 0111 } else { 0112 ret = Document::PrintToFilePrintError; 0113 } 0114 0115 if (doDeleteFile) { 0116 QFile::remove(fileList[0]); 0117 } 0118 0119 } else { // Print to a printer via lpr command 0120 0121 // Decide what executable to use to print with, need the CUPS version of lpr if available 0122 // Some distros name the CUPS version of lpr as lpr-cups or lpr.cups so try those first 0123 // before default to lpr, or failing that to lp 0124 0125 if (!QStandardPaths::findExecutable(QStringLiteral("lpr-cups")).isEmpty()) { 0126 exe = QStringLiteral("lpr-cups"); 0127 } else if (!QStandardPaths::findExecutable(QStringLiteral("lpr.cups")).isEmpty()) { 0128 exe = QStringLiteral("lpr.cups"); 0129 } else if (!QStandardPaths::findExecutable(QStringLiteral("lpr")).isEmpty()) { 0130 exe = QStringLiteral("lpr"); 0131 } else if (!QStandardPaths::findExecutable(QStringLiteral("lp")).isEmpty()) { 0132 exe = QStringLiteral("lp"); 0133 } else { 0134 return Document::NoBinaryToPrintError; 0135 } 0136 0137 bool useCupsOptions = cupsAvailable(); 0138 argList = printArguments(printer, fileDeletePolicy, pageSelectPolicy, useCupsOptions, pageRange, exe, documentOrientation, scaleMode) << fileList; 0139 qCDebug(OkularCoreDebug) << "Executing" << exe << "with arguments" << argList; 0140 0141 ret = doKProcessExecute(exe, argList); 0142 } 0143 0144 return ret; 0145 } 0146 0147 QList<int> FilePrinter::pageList(QPrinter &printer, int lastPage, int currentPage, const QList<int> &selectedPageList) 0148 { 0149 if (printer.printRange() == QPrinter::Selection) { 0150 return selectedPageList; 0151 } 0152 0153 int startPage, endPage; 0154 QList<int> list; 0155 0156 if (printer.printRange() == QPrinter::PageRange) { 0157 startPage = printer.fromPage(); 0158 endPage = printer.toPage(); 0159 } else if (printer.printRange() == QPrinter::CurrentPage) { 0160 startPage = currentPage; 0161 endPage = currentPage; 0162 } else { // AllPages 0163 startPage = 1; 0164 endPage = lastPage; 0165 } 0166 0167 for (int i = startPage; i <= endPage; i++) { 0168 list << i; 0169 } 0170 0171 return list; 0172 } 0173 0174 bool FilePrinter::ps2pdfAvailable() 0175 { 0176 return (!QStandardPaths::findExecutable(QStringLiteral("ps2pdf")).isEmpty()); 0177 } 0178 0179 bool FilePrinter::pdf2psAvailable() 0180 { 0181 return (!QStandardPaths::findExecutable(QStringLiteral("pdf2ps")).isEmpty()); 0182 } 0183 0184 bool FilePrinter::cupsAvailable() 0185 { 0186 #if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) 0187 // Ideally we would have access to the private Qt method 0188 // QCUPSSupport::cupsAvailable() to do this as it is very complex routine. 0189 // However, if CUPS is available then QPrinter::supportsMultipleCopies() will always return true 0190 // whereas if CUPS is not available it will return false. 0191 // This behaviour is guaranteed never to change, so we can use it as a reliable substitute. 0192 QPrinter testPrinter; 0193 return testPrinter.supportsMultipleCopies(); 0194 #else 0195 return false; 0196 #endif 0197 } 0198 0199 QStringList FilePrinter::printArguments(QPrinter &printer, 0200 FileDeletePolicy fileDeletePolicy, 0201 PageSelectPolicy pageSelectPolicy, 0202 bool useCupsOptions, 0203 const QString &pageRange, 0204 const QString &version, 0205 QPageLayout::Orientation documentOrientation, 0206 ScaleMode scaleMode) 0207 { 0208 QStringList argList; 0209 0210 if (!destination(printer, version).isEmpty()) { 0211 argList << destination(printer, version); 0212 } 0213 0214 if (!copies(printer, version).isEmpty()) { 0215 argList << copies(printer, version); 0216 } 0217 0218 if (!jobname(printer, version).isEmpty()) { 0219 argList << jobname(printer, version); 0220 } 0221 0222 if (!pages(printer, pageSelectPolicy, pageRange, useCupsOptions, version).isEmpty()) { 0223 argList << pages(printer, pageSelectPolicy, pageRange, useCupsOptions, version); 0224 } 0225 0226 if (useCupsOptions && !cupsOptions(printer, documentOrientation, scaleMode).isEmpty()) { 0227 argList << cupsOptions(printer, documentOrientation, scaleMode); 0228 } 0229 0230 if (!deleteFile(printer, fileDeletePolicy, version).isEmpty()) { 0231 argList << deleteFile(printer, fileDeletePolicy, version); 0232 } 0233 0234 if (version == QLatin1String("lp")) { 0235 argList << QStringLiteral("--"); 0236 } 0237 0238 return argList; 0239 } 0240 0241 QStringList FilePrinter::destination(QPrinter &printer, const QString &version) 0242 { 0243 if (version == QLatin1String("lp")) { 0244 return QStringList(QStringLiteral("-d")) << printer.printerName(); 0245 } 0246 0247 if (version.startsWith(QLatin1String("lpr"))) { 0248 return QStringList(QStringLiteral("-P")) << printer.printerName(); 0249 } 0250 0251 return QStringList(); 0252 } 0253 0254 QStringList FilePrinter::copies(QPrinter &printer, const QString &version) 0255 { 0256 int cp = printer.copyCount(); 0257 0258 if (version == QLatin1String("lp")) { 0259 return QStringList(QStringLiteral("-n")) << QStringLiteral("%1").arg(cp); 0260 } 0261 0262 if (version.startsWith(QLatin1String("lpr"))) { 0263 return QStringList() << QStringLiteral("-#%1").arg(cp); 0264 } 0265 0266 return QStringList(); 0267 } 0268 0269 QStringList FilePrinter::jobname(QPrinter &printer, const QString &version) 0270 { 0271 if (!printer.docName().isEmpty()) { 0272 if (version == QLatin1String("lp")) { 0273 return QStringList(QStringLiteral("-t")) << printer.docName(); 0274 } 0275 0276 if (version.startsWith(QLatin1String("lpr"))) { 0277 const QString shortenedDocName = QString::fromUtf8(printer.docName().toUtf8().left(255)); 0278 return QStringList(QStringLiteral("-J")) << shortenedDocName; 0279 } 0280 } 0281 0282 return QStringList(); 0283 } 0284 0285 QStringList FilePrinter::deleteFile(QPrinter &, FileDeletePolicy fileDeletePolicy, const QString &version) 0286 { 0287 if (fileDeletePolicy == FilePrinter::SystemDeletesFiles && version.startsWith(QLatin1String("lpr"))) { 0288 return QStringList(QStringLiteral("-r")); 0289 } 0290 0291 return QStringList(); 0292 } 0293 0294 QStringList FilePrinter::pages(QPrinter &printer, PageSelectPolicy pageSelectPolicy, const QString &pageRange, bool useCupsOptions, const QString &version) 0295 { 0296 if (pageSelectPolicy == FilePrinter::SystemSelectsPages) { 0297 if (printer.printRange() == QPrinter::Selection && !pageRange.isEmpty()) { 0298 if (version == QLatin1String("lp")) { 0299 return QStringList(QStringLiteral("-P")) << pageRange; 0300 } 0301 0302 if (version.startsWith(QLatin1String("lpr")) && useCupsOptions) { 0303 return QStringList(QStringLiteral("-o")) << QStringLiteral("page-ranges=%1").arg(pageRange); 0304 } 0305 } 0306 0307 if (printer.printRange() == QPrinter::PageRange) { 0308 if (version == QLatin1String("lp")) { 0309 return QStringList(QStringLiteral("-P")) << QStringLiteral("%1-%2").arg(printer.fromPage()).arg(printer.toPage()); 0310 } 0311 0312 if (version.startsWith(QLatin1String("lpr")) && useCupsOptions) { 0313 return QStringList(QStringLiteral("-o")) << QStringLiteral("page-ranges=%1-%2").arg(printer.fromPage()).arg(printer.toPage()); 0314 } 0315 } 0316 } 0317 0318 return QStringList(); // AllPages 0319 } 0320 0321 QStringList FilePrinter::cupsOptions(QPrinter &printer, QPageLayout::Orientation documentOrientation, ScaleMode scaleMode) 0322 { 0323 QStringList optionList; 0324 0325 if (!optionMedia(printer).isEmpty()) { 0326 optionList << optionMedia(printer); 0327 } 0328 0329 if (!optionOrientation(printer, documentOrientation).isEmpty()) { 0330 optionList << optionOrientation(printer, documentOrientation); 0331 } 0332 0333 if (!optionDoubleSidedPrinting(printer).isEmpty()) { 0334 optionList << optionDoubleSidedPrinting(printer); 0335 } 0336 0337 if (!optionPageOrder(printer).isEmpty()) { 0338 optionList << optionPageOrder(printer); 0339 } 0340 0341 if (!optionCollateCopies(printer).isEmpty()) { 0342 optionList << optionCollateCopies(printer); 0343 } 0344 0345 if (!optionPageMargins(printer, scaleMode).isEmpty()) { 0346 optionList << optionPageMargins(printer, scaleMode); 0347 } 0348 0349 optionList << optionCupsProperties(printer); 0350 0351 return optionList; 0352 } 0353 0354 QStringList FilePrinter::optionMedia(QPrinter &printer) 0355 { 0356 if (!mediaPageSize(printer).isEmpty() && !mediaPaperSource(printer).isEmpty()) { 0357 return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1,%2").arg(mediaPageSize(printer), mediaPaperSource(printer)); 0358 } 0359 0360 if (!mediaPageSize(printer).isEmpty()) { 0361 return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1").arg(mediaPageSize(printer)); 0362 } 0363 0364 if (!mediaPaperSource(printer).isEmpty()) { 0365 return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1").arg(mediaPaperSource(printer)); 0366 } 0367 0368 return QStringList(); 0369 } 0370 0371 QString FilePrinter::mediaPageSize(QPrinter &printer) 0372 { 0373 switch (printer.pageLayout().pageSize().id()) { 0374 case QPageSize::A0: 0375 return QStringLiteral("A0"); 0376 case QPageSize::A1: 0377 return QStringLiteral("A1"); 0378 case QPageSize::A2: 0379 return QStringLiteral("A2"); 0380 case QPageSize::A3: 0381 return QStringLiteral("A3"); 0382 case QPageSize::A4: 0383 return QStringLiteral("A4"); 0384 case QPageSize::A5: 0385 return QStringLiteral("A5"); 0386 case QPageSize::A6: 0387 return QStringLiteral("A6"); 0388 case QPageSize::A7: 0389 return QStringLiteral("A7"); 0390 case QPageSize::A8: 0391 return QStringLiteral("A8"); 0392 case QPageSize::A9: 0393 return QStringLiteral("A9"); 0394 case QPageSize::B0: 0395 return QStringLiteral("B0"); 0396 case QPageSize::B1: 0397 return QStringLiteral("B1"); 0398 case QPageSize::B10: 0399 return QStringLiteral("B10"); 0400 case QPageSize::B2: 0401 return QStringLiteral("B2"); 0402 case QPageSize::B3: 0403 return QStringLiteral("B3"); 0404 case QPageSize::B4: 0405 return QStringLiteral("B4"); 0406 case QPageSize::B5: 0407 return QStringLiteral("B5"); 0408 case QPageSize::B6: 0409 return QStringLiteral("B6"); 0410 case QPageSize::B7: 0411 return QStringLiteral("B7"); 0412 case QPageSize::B8: 0413 return QStringLiteral("B8"); 0414 case QPageSize::B9: 0415 return QStringLiteral("B9"); 0416 case QPageSize::C5E: 0417 return QStringLiteral("C5"); // Correct Translation? 0418 case QPageSize::Comm10E: 0419 return QStringLiteral("Comm10"); // Correct Translation? 0420 case QPageSize::DLE: 0421 return QStringLiteral("DL"); // Correct Translation? 0422 case QPageSize::Executive: 0423 return QStringLiteral("Executive"); 0424 case QPageSize::Folio: 0425 return QStringLiteral("Folio"); 0426 case QPageSize::Ledger: 0427 return QStringLiteral("Ledger"); 0428 case QPageSize::Legal: 0429 return QStringLiteral("Legal"); 0430 case QPageSize::Letter: 0431 return QStringLiteral("Letter"); 0432 case QPageSize::Tabloid: 0433 return QStringLiteral("Tabloid"); 0434 case QPageSize::Custom: 0435 return QStringLiteral("Custom.%1x%2mm").arg(printer.widthMM()).arg(printer.heightMM()); 0436 default: 0437 return QString(); 0438 } 0439 } 0440 0441 // What about Upper and MultiPurpose? And others in PPD??? 0442 QString FilePrinter::mediaPaperSource(QPrinter &printer) 0443 { 0444 switch (printer.paperSource()) { 0445 case QPrinter::Auto: 0446 return QString(); 0447 case QPrinter::Cassette: 0448 return QStringLiteral("Cassette"); 0449 case QPrinter::Envelope: 0450 return QStringLiteral("Envelope"); 0451 case QPrinter::EnvelopeManual: 0452 return QStringLiteral("EnvelopeManual"); 0453 case QPrinter::FormSource: 0454 return QStringLiteral("FormSource"); 0455 case QPrinter::LargeCapacity: 0456 return QStringLiteral("LargeCapacity"); 0457 case QPrinter::LargeFormat: 0458 return QStringLiteral("LargeFormat"); 0459 case QPrinter::Lower: 0460 return QStringLiteral("Lower"); 0461 case QPrinter::MaxPageSource: 0462 return QStringLiteral("MaxPageSource"); 0463 case QPrinter::Middle: 0464 return QStringLiteral("Middle"); 0465 case QPrinter::Manual: 0466 return QStringLiteral("Manual"); 0467 case QPrinter::OnlyOne: 0468 return QStringLiteral("OnlyOne"); 0469 case QPrinter::Tractor: 0470 return QStringLiteral("Tractor"); 0471 case QPrinter::SmallFormat: 0472 return QStringLiteral("SmallFormat"); 0473 default: 0474 return QString(); 0475 } 0476 } 0477 0478 QStringList FilePrinter::optionOrientation(QPrinter &printer, QPageLayout::Orientation documentOrientation) 0479 { 0480 // portrait and landscape options rotate the document according to the document orientation 0481 // If we want to print a landscape document as one would expect it, we have to pass the 0482 // portrait option so that the document is not rotated additionally 0483 if (printer.pageLayout().orientation() == documentOrientation) { 0484 // the user wants the document printed as is 0485 return QStringList(QStringLiteral("-o")) << QStringLiteral("portrait"); 0486 } else { 0487 // the user expects the document being rotated by 90 degrees 0488 return QStringList(QStringLiteral("-o")) << QStringLiteral("landscape"); 0489 } 0490 } 0491 0492 QStringList FilePrinter::optionDoubleSidedPrinting(QPrinter &printer) 0493 { 0494 switch (printer.duplex()) { 0495 case QPrinter::DuplexNone: 0496 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=one-sided"); 0497 case QPrinter::DuplexAuto: 0498 if (printer.pageLayout().orientation() == QPageLayout::Landscape) { 0499 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-short-edge"); 0500 } else { 0501 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-long-edge"); 0502 } 0503 case QPrinter::DuplexLongSide: 0504 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-long-edge"); 0505 case QPrinter::DuplexShortSide: 0506 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-short-edge"); 0507 default: 0508 return QStringList(); // Use printer default 0509 } 0510 } 0511 0512 QStringList FilePrinter::optionPageOrder(QPrinter &printer) 0513 { 0514 if (printer.pageOrder() == QPrinter::LastPageFirst) { 0515 return QStringList(QStringLiteral("-o")) << QStringLiteral("outputorder=reverse"); 0516 } 0517 return QStringList(QStringLiteral("-o")) << QStringLiteral("outputorder=normal"); 0518 } 0519 0520 QStringList FilePrinter::optionCollateCopies(QPrinter &printer) 0521 { 0522 if (printer.collateCopies()) { 0523 return QStringList(QStringLiteral("-o")) << QStringLiteral("Collate=True"); 0524 } 0525 return QStringList(QStringLiteral("-o")) << QStringLiteral("Collate=False"); 0526 } 0527 0528 QStringList FilePrinter::optionPageMargins(QPrinter &printer, ScaleMode scaleMode) 0529 { 0530 if (printer.printEngine()->property(QPrintEngine::PPK_PageMargins).isNull()) { 0531 return QStringList(); 0532 } else { 0533 qreal l(0), t(0), r(0), b(0); 0534 if (!printer.fullPage()) { 0535 auto marginsf = printer.pageLayout().margins(QPageLayout::Point); 0536 l = marginsf.left(); 0537 t = marginsf.top(); 0538 r = marginsf.right(); 0539 b = marginsf.bottom(); 0540 } 0541 QStringList marginOptions; 0542 marginOptions << (QStringLiteral("-o")) << QStringLiteral("page-left=%1").arg(l) << QStringLiteral("-o") << QStringLiteral("page-top=%1").arg(t) << QStringLiteral("-o") << QStringLiteral("page-right=%1").arg(r) 0543 << QStringLiteral("-o") << QStringLiteral("page-bottom=%1").arg(b); 0544 if (scaleMode == ScaleMode::FitToPrintArea) { 0545 marginOptions << QStringLiteral("-o") << QStringLiteral("fit-to-page"); 0546 } 0547 0548 return marginOptions; 0549 } 0550 } 0551 0552 QStringList FilePrinter::optionCupsProperties(QPrinter &printer) 0553 { 0554 QStringList dialogOptions = printer.printEngine()->property(QPrintEngine::PrintEnginePropertyKey(0xfe00)).toStringList(); 0555 QStringList cupsOptions; 0556 0557 for (int i = 0; i < dialogOptions.count(); i = i + 2) { 0558 if (dialogOptions[i + 1].isEmpty()) { 0559 cupsOptions << QStringLiteral("-o") << dialogOptions[i]; 0560 } else { 0561 cupsOptions << QStringLiteral("-o") << dialogOptions[i] + QLatin1Char('=') + dialogOptions[i + 1]; 0562 } 0563 } 0564 0565 return cupsOptions; 0566 } 0567 0568 /* kate: replace-tabs on; indent-width 4; */