Okular

fileprinter.cpp
1/*
2 SPDX-FileCopyrightText: 2007, 2010 John Layt <john@layt.net>
3
4 FilePrinterPreview based on KPrintPreview (originally LGPL)
5 SPDX-FileCopyrightText: 2007 Alex Merry <huntedhacker@tiscali.co.uk>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "fileprinter.h"
11
12#include <QFile>
13#include <QFileInfo>
14#include <QLabel>
15#include <QPrintEngine>
16#include <QShowEvent>
17#include <QSize>
18#include <QStringList>
19#include <QTcpSocket>
20
21#include <KProcess>
22#include <KShell>
23#include <QDebug>
24#include <QStandardPaths>
25
26#include "debug_p.h"
27
28using namespace Okular;
29
31FilePrinter::printFile(QPrinter &printer, const QString &file, QPageLayout::Orientation documentOrientation, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, ScaleMode scaleMode)
32{
33 FilePrinter fp;
34 return fp.doPrintFiles(printer, QStringList(file), fileDeletePolicy, pageSelectPolicy, pageRange, documentOrientation, scaleMode);
35}
36
37static Document::PrintError doKProcessExecute(const QString &exe, const QStringList &argList)
38{
39 const int ret = KProcess::execute(exe, argList);
40 if (ret == -1) {
41 return Document::PrintingProcessCrashPrintError;
42 }
43 if (ret == -2) {
44 return Document::PrintingProcessStartPrintError;
45 }
46 if (ret < 0) {
47 return Document::UnknownPrintError;
48 }
49
51}
52
54FilePrinter::doPrintFiles(QPrinter &printer, const QStringList &fileList, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, QPageLayout::Orientation documentOrientation, ScaleMode scaleMode)
55{
56 if (fileList.size() < 1) {
57 return Document::NoFileToPrintError;
58 }
59
60 for (QStringList::ConstIterator it = fileList.constBegin(); it != fileList.constEnd(); ++it) {
61 if (!QFile::exists(*it)) {
62 return Document::UnableToFindFilePrintError;
63 }
64 }
65
66 if (printer.printerState() == QPrinter::Aborted || printer.printerState() == QPrinter::Error) {
67 return Document::InvalidPrinterStatePrintError;
68 }
69
70 QString exe;
71 QStringList argList;
73
74 // Print to File if a filename set, assumes there must be only 1 file
75 if (!printer.outputFileName().isEmpty()) {
76 if (QFile::exists(printer.outputFileName())) {
78 }
79
80 QFileInfo inputFileInfo = QFileInfo(fileList[0]);
81 QFileInfo outputFileInfo = QFileInfo(printer.outputFileName());
82
83 bool doDeleteFile = (fileDeletePolicy == FilePrinter::SystemDeletesFiles);
84 if (inputFileInfo.suffix() == outputFileInfo.suffix()) {
85 if (doDeleteFile) {
86 bool res = QFile::rename(fileList[0], printer.outputFileName());
87 if (res) {
88 doDeleteFile = false;
90 } else {
91 ret = Document::PrintToFilePrintError;
92 }
93 } else {
94 bool res = QFile::copy(fileList[0], printer.outputFileName());
95 if (res) {
97 } else {
98 ret = Document::PrintToFilePrintError;
99 }
100 }
101 } else if (inputFileInfo.suffix() == QLatin1String("ps") && printer.outputFormat() == QPrinter::PdfFormat && ps2pdfAvailable()) {
102 exe = QStringLiteral("ps2pdf");
103 argList << fileList[0] << printer.outputFileName();
104 qCDebug(OkularCoreDebug) << "Executing" << exe << "with arguments" << argList;
105 ret = doKProcessExecute(exe, argList);
106 } else if (inputFileInfo.suffix() == QLatin1String("pdf") && printer.outputFormat() == QPrinter::NativeFormat && pdf2psAvailable()) {
107 exe = QStringLiteral("pdf2ps");
108 argList << fileList[0] << printer.outputFileName();
109 qCDebug(OkularCoreDebug) << "Executing" << exe << "with arguments" << argList;
110 ret = doKProcessExecute(exe, argList);
111 } else {
112 ret = Document::PrintToFilePrintError;
113 }
114
115 if (doDeleteFile) {
116 QFile::remove(fileList[0]);
117 }
118
119 } else { // Print to a printer via lpr command
120
121 // Decide what executable to use to print with, need the CUPS version of lpr if available
122 // Some distros name the CUPS version of lpr as lpr-cups or lpr.cups so try those first
123 // before default to lpr, or failing that to lp
124
125 if (!QStandardPaths::findExecutable(QStringLiteral("lpr-cups")).isEmpty()) {
126 exe = QStringLiteral("lpr-cups");
127 } else if (!QStandardPaths::findExecutable(QStringLiteral("lpr.cups")).isEmpty()) {
128 exe = QStringLiteral("lpr.cups");
129 } else if (!QStandardPaths::findExecutable(QStringLiteral("lpr")).isEmpty()) {
130 exe = QStringLiteral("lpr");
131 } else if (!QStandardPaths::findExecutable(QStringLiteral("lp")).isEmpty()) {
132 exe = QStringLiteral("lp");
133 } else {
134 return Document::NoBinaryToPrintError;
135 }
136
137 bool useCupsOptions = cupsAvailable();
138 argList = printArguments(printer, fileDeletePolicy, pageSelectPolicy, useCupsOptions, pageRange, exe, documentOrientation, scaleMode) << fileList;
139 qCDebug(OkularCoreDebug) << "Executing" << exe << "with arguments" << argList;
140
141 ret = doKProcessExecute(exe, argList);
142 }
143
144 return ret;
145}
146
147QList<int> FilePrinter::pageList(QPrinter &printer, int lastPage, int currentPage, const QList<int> &selectedPageList)
148{
149 if (printer.printRange() == QPrinter::Selection) {
150 return selectedPageList;
151 }
152
153 int startPage, endPage;
155
156 if (printer.printRange() == QPrinter::PageRange) {
157 startPage = printer.fromPage();
158 endPage = printer.toPage();
159 } else if (printer.printRange() == QPrinter::CurrentPage) {
160 startPage = currentPage;
161 endPage = currentPage;
162 } else { // AllPages
163 startPage = 1;
164 endPage = lastPage;
165 }
166
167 for (int i = startPage; i <= endPage; i++) {
168 list << i;
169 }
170
171 return list;
172}
173
174bool FilePrinter::ps2pdfAvailable()
175{
176 return (!QStandardPaths::findExecutable(QStringLiteral("ps2pdf")).isEmpty());
177}
178
179bool FilePrinter::pdf2psAvailable()
180{
181 return (!QStandardPaths::findExecutable(QStringLiteral("pdf2ps")).isEmpty());
182}
183
184bool FilePrinter::cupsAvailable()
185{
186#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
187 // Ideally we would have access to the private Qt method
188 // QCUPSSupport::cupsAvailable() to do this as it is very complex routine.
189 // However, if CUPS is available then QPrinter::supportsMultipleCopies() will always return true
190 // whereas if CUPS is not available it will return false.
191 // This behaviour is guaranteed never to change, so we can use it as a reliable substitute.
192 QPrinter testPrinter;
193 return testPrinter.supportsMultipleCopies();
194#else
195 return false;
196#endif
197}
198
199QStringList FilePrinter::printArguments(QPrinter &printer,
200 FileDeletePolicy fileDeletePolicy,
201 PageSelectPolicy pageSelectPolicy,
202 bool useCupsOptions,
203 const QString &pageRange,
204 const QString &version,
205 QPageLayout::Orientation documentOrientation,
206 ScaleMode scaleMode)
207{
208 QStringList argList;
209
210 if (!destination(printer, version).isEmpty()) {
211 argList << destination(printer, version);
212 }
213
214 if (!copies(printer, version).isEmpty()) {
215 argList << copies(printer, version);
216 }
217
218 if (!jobname(printer, version).isEmpty()) {
219 argList << jobname(printer, version);
220 }
221
222 if (!pages(printer, pageSelectPolicy, pageRange, useCupsOptions, version).isEmpty()) {
223 argList << pages(printer, pageSelectPolicy, pageRange, useCupsOptions, version);
224 }
225
226 if (useCupsOptions && !cupsOptions(printer, documentOrientation, scaleMode).isEmpty()) {
227 argList << cupsOptions(printer, documentOrientation, scaleMode);
228 }
229
230 if (!deleteFile(printer, fileDeletePolicy, version).isEmpty()) {
231 argList << deleteFile(printer, fileDeletePolicy, version);
232 }
233
234 if (version == QLatin1String("lp")) {
235 argList << QStringLiteral("--");
236 }
237
238 return argList;
239}
240
241QStringList FilePrinter::destination(QPrinter &printer, const QString &version)
242{
243 if (version == QLatin1String("lp")) {
244 return QStringList(QStringLiteral("-d")) << printer.printerName();
245 }
246
247 if (version.startsWith(QLatin1String("lpr"))) {
248 return QStringList(QStringLiteral("-P")) << printer.printerName();
249 }
250
251 return QStringList();
252}
253
254QStringList FilePrinter::copies(QPrinter &printer, const QString &version)
255{
256 int cp = printer.copyCount();
257
258 if (version == QLatin1String("lp")) {
259 return QStringList(QStringLiteral("-n")) << QStringLiteral("%1").arg(cp);
260 }
261
262 if (version.startsWith(QLatin1String("lpr"))) {
263 return QStringList() << QStringLiteral("-#%1").arg(cp);
264 }
265
266 return QStringList();
267}
268
269QStringList FilePrinter::jobname(QPrinter &printer, const QString &version)
270{
271 if (!printer.docName().isEmpty()) {
272 if (version == QLatin1String("lp")) {
273 return QStringList(QStringLiteral("-t")) << printer.docName();
274 }
275
276 if (version.startsWith(QLatin1String("lpr"))) {
277 const QString shortenedDocName = QString::fromUtf8(printer.docName().toUtf8().left(255));
278 return QStringList(QStringLiteral("-J")) << shortenedDocName;
279 }
280 }
281
282 return QStringList();
283}
284
285QStringList FilePrinter::deleteFile(QPrinter &, FileDeletePolicy fileDeletePolicy, const QString &version)
286{
287 if (fileDeletePolicy == FilePrinter::SystemDeletesFiles && version.startsWith(QLatin1String("lpr"))) {
288 return QStringList(QStringLiteral("-r"));
289 }
290
291 return QStringList();
292}
293
294QStringList FilePrinter::pages(QPrinter &printer, PageSelectPolicy pageSelectPolicy, const QString &pageRange, bool useCupsOptions, const QString &version)
295{
296 if (pageSelectPolicy == FilePrinter::SystemSelectsPages) {
297 if (printer.printRange() == QPrinter::Selection && !pageRange.isEmpty()) {
298 if (version == QLatin1String("lp")) {
299 return QStringList(QStringLiteral("-P")) << pageRange;
300 }
301
302 if (version.startsWith(QLatin1String("lpr")) && useCupsOptions) {
303 return QStringList(QStringLiteral("-o")) << QStringLiteral("page-ranges=%1").arg(pageRange);
304 }
305 }
306
307 if (printer.printRange() == QPrinter::PageRange) {
308 if (version == QLatin1String("lp")) {
309 return QStringList(QStringLiteral("-P")) << QStringLiteral("%1-%2").arg(printer.fromPage()).arg(printer.toPage());
310 }
311
312 if (version.startsWith(QLatin1String("lpr")) && useCupsOptions) {
313 return QStringList(QStringLiteral("-o")) << QStringLiteral("page-ranges=%1-%2").arg(printer.fromPage()).arg(printer.toPage());
314 }
315 }
316 }
317
318 return QStringList(); // AllPages
319}
320
321QStringList FilePrinter::cupsOptions(QPrinter &printer, QPageLayout::Orientation documentOrientation, ScaleMode scaleMode)
322{
323 QStringList optionList;
324
325 if (!optionMedia(printer).isEmpty()) {
326 optionList << optionMedia(printer);
327 }
328
329 if (!optionOrientation(printer, documentOrientation).isEmpty()) {
330 optionList << optionOrientation(printer, documentOrientation);
331 }
332
333 if (!optionDoubleSidedPrinting(printer).isEmpty()) {
334 optionList << optionDoubleSidedPrinting(printer);
335 }
336
337 if (!optionPageOrder(printer).isEmpty()) {
338 optionList << optionPageOrder(printer);
339 }
340
341 if (!optionCollateCopies(printer).isEmpty()) {
342 optionList << optionCollateCopies(printer);
343 }
344
345 if (!optionPageMargins(printer, scaleMode).isEmpty()) {
346 optionList << optionPageMargins(printer, scaleMode);
347 }
348
349 optionList << optionCupsProperties(printer);
350
351 return optionList;
352}
353
354QStringList FilePrinter::optionMedia(QPrinter &printer)
355{
356 if (!mediaPageSize(printer).isEmpty() && !mediaPaperSource(printer).isEmpty()) {
357 return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1,%2").arg(mediaPageSize(printer), mediaPaperSource(printer));
358 }
359
360 if (!mediaPageSize(printer).isEmpty()) {
361 return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1").arg(mediaPageSize(printer));
362 }
363
364 if (!mediaPaperSource(printer).isEmpty()) {
365 return QStringList(QStringLiteral("-o")) << QStringLiteral("media=%1").arg(mediaPaperSource(printer));
366 }
367
368 return QStringList();
369}
370
371QString FilePrinter::mediaPageSize(QPrinter &printer)
372{
373 switch (printer.pageLayout().pageSize().id()) {
374 case QPageSize::A0:
375 return QStringLiteral("A0");
376 case QPageSize::A1:
377 return QStringLiteral("A1");
378 case QPageSize::A2:
379 return QStringLiteral("A2");
380 case QPageSize::A3:
381 return QStringLiteral("A3");
382 case QPageSize::A4:
383 return QStringLiteral("A4");
384 case QPageSize::A5:
385 return QStringLiteral("A5");
386 case QPageSize::A6:
387 return QStringLiteral("A6");
388 case QPageSize::A7:
389 return QStringLiteral("A7");
390 case QPageSize::A8:
391 return QStringLiteral("A8");
392 case QPageSize::A9:
393 return QStringLiteral("A9");
394 case QPageSize::B0:
395 return QStringLiteral("B0");
396 case QPageSize::B1:
397 return QStringLiteral("B1");
398 case QPageSize::B10:
399 return QStringLiteral("B10");
400 case QPageSize::B2:
401 return QStringLiteral("B2");
402 case QPageSize::B3:
403 return QStringLiteral("B3");
404 case QPageSize::B4:
405 return QStringLiteral("B4");
406 case QPageSize::B5:
407 return QStringLiteral("B5");
408 case QPageSize::B6:
409 return QStringLiteral("B6");
410 case QPageSize::B7:
411 return QStringLiteral("B7");
412 case QPageSize::B8:
413 return QStringLiteral("B8");
414 case QPageSize::B9:
415 return QStringLiteral("B9");
416 case QPageSize::C5E:
417 return QStringLiteral("C5"); // Correct Translation?
419 return QStringLiteral("Comm10"); // Correct Translation?
420 case QPageSize::DLE:
421 return QStringLiteral("DL"); // Correct Translation?
423 return QStringLiteral("Executive");
424 case QPageSize::Folio:
425 return QStringLiteral("Folio");
427 return QStringLiteral("Ledger");
428 case QPageSize::Legal:
429 return QStringLiteral("Legal");
431 return QStringLiteral("Letter");
433 return QStringLiteral("Tabloid");
435 return QStringLiteral("Custom.%1x%2mm").arg(printer.widthMM()).arg(printer.heightMM());
436 default:
437 return QString();
438 }
439}
440
441// What about Upper and MultiPurpose? And others in PPD???
442QString FilePrinter::mediaPaperSource(QPrinter &printer)
443{
444 switch (printer.paperSource()) {
445 case QPrinter::Auto:
446 return QString();
448 return QStringLiteral("Cassette");
450 return QStringLiteral("Envelope");
452 return QStringLiteral("EnvelopeManual");
454 return QStringLiteral("FormSource");
456 return QStringLiteral("LargeCapacity");
458 return QStringLiteral("LargeFormat");
459 case QPrinter::Lower:
460 return QStringLiteral("Lower");
462 return QStringLiteral("MaxPageSource");
463 case QPrinter::Middle:
464 return QStringLiteral("Middle");
465 case QPrinter::Manual:
466 return QStringLiteral("Manual");
468 return QStringLiteral("OnlyOne");
470 return QStringLiteral("Tractor");
472 return QStringLiteral("SmallFormat");
473 default:
474 return QString();
475 }
476}
477
478QStringList FilePrinter::optionOrientation(QPrinter &printer, QPageLayout::Orientation documentOrientation)
479{
480 // portrait and landscape options rotate the document according to the document orientation
481 // If we want to print a landscape document as one would expect it, we have to pass the
482 // portrait option so that the document is not rotated additionally
483 if (printer.pageLayout().orientation() == documentOrientation) {
484 // the user wants the document printed as is
485 return QStringList(QStringLiteral("-o")) << QStringLiteral("portrait");
486 } else {
487 // the user expects the document being rotated by 90 degrees
488 return QStringList(QStringLiteral("-o")) << QStringLiteral("landscape");
489 }
490}
491
492QStringList FilePrinter::optionDoubleSidedPrinting(QPrinter &printer)
493{
494 switch (printer.duplex()) {
496 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=one-sided");
498 if (printer.pageLayout().orientation() == QPageLayout::Landscape) {
499 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-short-edge");
500 } else {
501 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-long-edge");
502 }
504 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-long-edge");
506 return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-short-edge");
507 default:
508 return QStringList(); // Use printer default
509 }
510}
511
512QStringList FilePrinter::optionPageOrder(QPrinter &printer)
513{
514 if (printer.pageOrder() == QPrinter::LastPageFirst) {
515 return QStringList(QStringLiteral("-o")) << QStringLiteral("outputorder=reverse");
516 }
517 return QStringList(QStringLiteral("-o")) << QStringLiteral("outputorder=normal");
518}
519
520QStringList FilePrinter::optionCollateCopies(QPrinter &printer)
521{
522 if (printer.collateCopies()) {
523 return QStringList(QStringLiteral("-o")) << QStringLiteral("Collate=True");
524 }
525 return QStringList(QStringLiteral("-o")) << QStringLiteral("Collate=False");
526}
527
528QStringList FilePrinter::optionPageMargins(QPrinter &printer, ScaleMode scaleMode)
529{
531 return QStringList();
532 } else {
533 qreal l(0), t(0), r(0), b(0);
534 if (!printer.fullPage()) {
535 auto marginsf = printer.pageLayout().margins(QPageLayout::Point);
536 l = marginsf.left();
537 t = marginsf.top();
538 r = marginsf.right();
539 b = marginsf.bottom();
540 }
541 QStringList marginOptions;
542 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)
543 << QStringLiteral("-o") << QStringLiteral("page-bottom=%1").arg(b);
544 if (scaleMode == ScaleMode::FitToPrintArea) {
545 marginOptions << QStringLiteral("-o") << QStringLiteral("fit-to-page");
546 }
547
548 return marginOptions;
549 }
550}
551
552QStringList FilePrinter::optionCupsProperties(QPrinter &printer)
553{
555 QStringList cupsOptions;
556
557 for (int i = 0; i < dialogOptions.count(); i = i + 2) {
558 if (dialogOptions[i + 1].isEmpty()) {
559 cupsOptions << QStringLiteral("-o") << dialogOptions[i];
560 } else {
561 cupsOptions << QStringLiteral("-o") << dialogOptions[i] + QLatin1Char('=') + dialogOptions[i + 1];
562 }
563 }
564
565 return cupsOptions;
566}
567
568/* kate: replace-tabs on; indent-width 4; */
int execute(int msecs=-1)
@ NoPrintError
Printing succeeded.
Definition document.h:839
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QAction * lastPage(const QObject *recvr, const char *slot, QObject *parent)
NETWORKMANAGERQT_EXPORT QString version()
global.h
Definition action.h:17
QByteArray left(qsizetype len) const const
bool copy(const QString &fileName, const QString &newName)
bool exists() const const
bool remove()
bool rename(const QString &newName)
QString suffix() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
qsizetype size() const const
qreal left() const const
QPageLayout pageLayout() const const
QMarginsF margins() const const
Orientation orientation() const const
QPageSize pageSize() const const
PageSizeId id(const QSize &pointSize, SizeMatchPolicy matchPolicy)
int heightMM() const const
int widthMM() const const
virtual QVariant property(PrintEnginePropertyKey key) const const=0
bool collateCopies() const const
int copyCount() const const
QString docName() const const
DuplexMode duplex() const const
int fromPage() const const
bool fullPage() const const
QString outputFileName() const const
OutputFormat outputFormat() const const
PageOrder pageOrder() const const
PaperSource paperSource() const const
QPrintEngine * printEngine() const const
PrintRange printRange() const const
QString printerName() const const
PrinterState printerState() const const
bool supportsMultipleCopies() const const
int toPage() const const
QString findExecutable(const QString &executableName, const QStringList &paths)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toUtf8() const const
bool isNull() const const
QStringList toStringList() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 20 2024 11:47:55 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.