eric6/WebBrowser/Tools/FilePrinter.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an object for printing of files.
8 """
9
10 #
11 # This code is inspired by and ported from Qupzilla.
12 # Original Copyright (C) 2016 by Kevin Kofler <kevin.kofler@chello.at>
13 #
14
15 from __future__ import unicode_literals
16
17 from PyQt5.QtCore import QFile, QStandardPaths, QProcess
18 from PyQt5.QtPrintSupport import QPrinter, QPrintEngine
19
20 import Globals
21 from Globals import qVersionTuple
22
23
24 _FilePrintJobs = []
25
26
27 class FilePrinter(object):
28 """
29 Class implementing methods for printing on *nix systems.
30 """
31 #
32 # Whether file(s) get deleted by the application or by the print system.
33 #
34 ApplicationDeletesFiles = 0
35 SystemDeletesFiles = 1
36
37 #
38 # Whether pages to be printed are selected by the application or the print
39 # system.
40 #
41 # If application side, then the generated file will only contain those
42 # pages selected by the user, so FilePrinter will print all the pages in
43 # the file.
44 #
45 # If system side, then the file will contain all the pages in the
46 # document, and the print system will print the users selected print range
47 # from out of the file.
48 #
49 # Note: system side only works in CUPS, not LPR.
50 #
51 ApplicationSelectsPages = 0
52 SystemSelectsPages = 1
53
54 def __init__(self):
55 """
56 Constructor
57 """
58 self.__paperSizesMap = {
59 QPrinter.A0: "A0",
60 QPrinter.A1: "A1",
61 QPrinter.A2: "A2",
62 QPrinter.A3: "A3",
63 QPrinter.A4: "A4",
64 QPrinter.A5: "A5",
65 QPrinter.A6: "A6",
66 QPrinter.A7: "A7",
67 QPrinter.A8: "A8",
68 QPrinter.A9: "A9",
69 QPrinter.B0: "B0",
70 QPrinter.B1: "B1",
71 QPrinter.B2: "B2",
72 QPrinter.B3: "B3",
73 QPrinter.B4: "B4",
74 QPrinter.B5: "B5",
75 QPrinter.B6: "B6",
76 QPrinter.B7: "B7",
77 QPrinter.B8: "B8",
78 QPrinter.B9: "B9",
79 QPrinter.B10: "B10",
80 QPrinter.C5E: "C5",
81 QPrinter.Comm10E: "Comm10",
82 QPrinter.DLE: "DL",
83 QPrinter.Executive: "Executive",
84 QPrinter.Folio: "Folio",
85 QPrinter.Ledger: "Ledger",
86 QPrinter.Legal: "Legal",
87 QPrinter.Letter: "Letter",
88 QPrinter.Tabloid: "Tabloid",
89 }
90
91 self.__paperSourcesMap = {
92 QPrinter.Auto: "",
93 QPrinter.Cassette: "Cassette",
94 QPrinter.Envelope: "Envelope",
95 QPrinter.EnvelopeManual: "EnvelopeManual",
96 QPrinter.FormSource: "FormSource",
97 QPrinter.LargeCapacity: "LargeCapacity",
98 QPrinter.LargeFormat: "LargeFormat",
99 QPrinter.Lower: "Lower",
100 QPrinter.MaxPageSource: "MaxPageSource",
101 QPrinter.Middle: "Middle",
102 QPrinter.Manual: "Manual",
103 QPrinter.OnlyOne: "OnlyOne",
104 QPrinter.Tractor: "Tractor",
105 QPrinter.SmallFormat: "SmallFormat",
106 }
107
108 self.__process = None
109 self.__doDeleteFile = FilePrinter.ApplicationDeletesFiles
110 self.__fileName = ""
111
112 def _doPrintFile(self, printer, fileName, fileDeletePolicy,
113 pageSelectPolicy, pageRange):
114 """
115 Protected method to print a file.
116
117 @param printer reference to the printer to print to
118 @type QPrinter
119 @param fileName name (path) of the file to be printed
120 @type str
121 @param fileDeletePolicy policy determining who deletes the file to be
122 printed (application or system)
123 @type int (0 or 1)
124 @param pageSelectPolicy policy determining who selects the pages to be
125 printed (application or system)
126 @type int (0 or 1)
127 @param pageRange string determining the page range(s) to be printed, if
128 SystemSelectsPages was given for pageSelectPolicy and user chose
129 Selection in print dialog
130 @type str
131 @return flag indicating successful print job submission
132 @rtype bool
133 """
134 if not QFile.exists(fileName):
135 return False
136
137 self.__fileName = fileName
138 self.__doDeleteFile = (
139 fileDeletePolicy == FilePrinter.SystemDeletesFiles)
140
141 if printer.printerState() in [QPrinter.Aborted, QPrinter.Error]:
142 if self.__doDeleteFile:
143 QFile.remove(fileName)
144 return False
145
146 #
147 # Print via lpr/lp command
148 #
149
150 #
151 # Decide what executable to use to print with, need the CUPS version
152 # of lpr if available. Some distros name the CUPS version of lpr as
153 # lpr-cups or lpr.cups so try those first before default to lpr, or
154 # failing that to lp.
155 if QStandardPaths.findExecutable("lpr-cups"):
156 exe = "lpr-cups"
157 elif QStandardPaths.findExecutable("lpr.cups"):
158 exe = "lpr.cups"
159 elif QStandardPaths.findExecutable("lpr"):
160 exe = "lpr"
161 elif QStandardPaths.findExecutable("lp"):
162 exe = "lp"
163 else:
164 if self.__doDeleteFile:
165 QFile.remove(fileName)
166 return False
167
168 useCupsOptions = isCupsAvailable()
169 argsList = self._printArguments(
170 printer, fileDeletePolicy, pageSelectPolicy, useCupsOptions,
171 pageRange, exe)
172 argsList.append(fileName)
173
174 self.__process = QProcess()
175 if qVersionTuple() < (5, 6, 0):
176 self.__process.error.connect(self.__processError)
177 else:
178 self.__process.errorOccurred.connect(self.__processError)
179 self.__process.finished.connect(self.__processFinished)
180 self.__process.start(exe, argsList)
181 if not self.__process.waitForStarted(10000):
182 # it failed to start
183 self.__doCleanup(self.__doDeleteFile)
184 return False
185
186 return True
187
188 def __doCleanup(self, deleteFile):
189 """
190 Private method to perform some internal cleanup actions.
191
192 @param deleteFile flag indicating to delete the print file
193 @type bool
194 """
195 if deleteFile:
196 QFile.remove(self.__fileName)
197
198 self.__process.deleteLater()
199 self.__process = None
200
201 if self in _FilePrintJobs:
202 _FilePrintJobs.remove(self)
203
204 def __processError(self, error):
205 """
206 Private slot handling process errors.
207
208 @param error error value
209 @type QProcess.ProcessError
210 """
211 self.__doCleanup(self.__doDeleteFile)
212
213 def __processFinished(self, exitCode, exitStatus):
214 """
215 Private slot handling the end of the process.
216
217 @param exitCode exit code of the process
218 @type int
219 @param exitStatus exit status of the process
220 @type QProcess.ExitStatus
221 """
222 self.__doCleanup(self.__doDeleteFile and (
223 exitStatus != QProcess.NormalExit or exitCode != 0))
224
225 def _printArguments(self, printer, fileDeletePolicy, pageSelectPolicy,
226 useCupsOptions, pageRange, variant):
227 """
228 Protected method to assemble the command line arguments for the print
229 command.
230
231 @param printer reference to the printer to print to
232 @type QPrinter
233 @param fileDeletePolicy policy determining who deletes the file to be
234 printed (application or system)
235 @type int (0 or 1)
236 @param pageSelectPolicy policy determining who selects the pages to be
237 printed (application or system)
238 @type int (0 or 1)
239 @param useCupsOptions flag indicating to assemble the arguments for
240 CUPS
241 @type bool
242 @param pageRange string determining the page range(s) to be printed, if
243 SystemSelectsPages was given for pageSelectPolicy and user chose
244 Selection in print dialog
245 @type str
246 @param variant string identifying the print command variant
247 @type str
248 @return assembled command line arguments for the print command
249 @rtype list of str
250 """
251 if variant.startswith("lpr"):
252 variant = "lpr"
253
254 args = []
255 args.extend(self._destination(printer, variant))
256 args.extend(self._copies(printer, variant))
257 args.extend(self._jobname(printer, variant))
258 args.extend(self._pages(printer, pageSelectPolicy, pageRange,
259 useCupsOptions, variant))
260 if useCupsOptions:
261 args.extend(self._cupsOptions(printer))
262 args.extend(self._deleteFile(printer, fileDeletePolicy, variant))
263 if variant == "lp":
264 args.append("--")
265
266 return args
267
268 def _destination(self, printer, variant):
269 """
270 Protected method to assemble the printer destination arguments.
271
272 @param printer reference to the printer to print to
273 @type QPrinter
274 @param variant string identifying the print command variant
275 @type str
276 @return assembled printer destination arguments
277 @rtype list of str
278 """
279 if variant == "lp":
280 return ["-d", printer.printerName()]
281 elif variant == "lpr":
282 return ["-P", printer.printerName()]
283 else:
284 return []
285
286 def _copies(self, printer, variant):
287 """
288 Protected method to assemble the number of copies arguments.
289
290 @param printer reference to the printer to print to
291 @type QPrinter
292 @param variant string identifying the print command variant
293 @type str
294 @return assembled number of copies arguments
295 @rtype list of str
296 """
297 copies = printer.copyCount()
298 if variant == "lp":
299 return ["-n", str(copies)]
300 elif variant == "lpr":
301 return ["-#{0}".format(copies)]
302 else:
303 return []
304
305 def _jobname(self, printer, variant):
306 """
307 Protected method to assemble the jobname arguments.
308
309 @param printer reference to the printer to print to
310 @type QPrinter
311 @param variant string identifying the print command variant
312 @type str
313 @return assembled jobname arguments
314 @rtype list of str
315 """
316 if printer.docName():
317 if variant == "lp":
318 return ["-t", printer.docName()]
319 elif variant == "lpr":
320 shortenedDocName = printer.docName()[:255]
321 return ["-J", shortenedDocName]
322
323 return []
324
325 def _deleteFile(self, printer, fileDeletePolicy, variant):
326 """
327 Protected method to assemble the jobname arguments.
328
329 @param printer reference to the printer to print to
330 @type QPrinter
331 @param fileDeletePolicy policy determining who deletes the file to be
332 printed (application or system)
333 @type int (0 or 1)
334 @param variant string identifying the print command variant
335 @type str
336 @return assembled jobname arguments
337 @rtype list of str
338 """
339 if fileDeletePolicy == FilePrinter.SystemDeletesFiles and \
340 variant == "lpr":
341 return ["-r"]
342 else:
343 return []
344
345 def _pages(self, printer, pageSelectPolicy, pageRange, useCupsOptions,
346 variant):
347 """
348 Protected method to assemble the page range(s) arguments.
349
350 @param printer reference to the printer to print to
351 @type QPrinter
352 @param pageSelectPolicy policy determining who selects the pages to be
353 printed (application or system)
354 @type int (0 or 1)
355 @param pageRange string determining the page range(s) to be printed, if
356 SystemSelectsPages was given for pageSelectPolicy and user chose
357 Selection in print dialog
358 @type str
359 @param useCupsOptions flag indicating to assemble the arguments for
360 CUPS
361 @type bool
362 @param variant string identifying the print command variant
363 @type str
364 @return assembled page range(s) arguments
365 @rtype list of str
366 """
367 if pageSelectPolicy == FilePrinter.SystemSelectsPages:
368 if printer.printRange() == QPrinter.Selection and bool(pageRange):
369 if variant == "lp":
370 return ["-P", pageRange]
371 elif variant == "lpr":
372 return ["-o", "page-ranges={0}".format(pageRange)]
373
374 if printer.printRange() == QPrinter.PageRange:
375 if variant == "lp":
376 return ["-P", "{0}-{1}".format(
377 printer.fromPage(), printer.toPage())]
378 elif variant == "lpr":
379 return ["-o", "page-ranges={0}-{1}".format(
380 printer.fromPage(), printer.toPage())]
381
382 return [] # all pages
383
384 def _cupsOptions(self, printer):
385 """
386 Protected method to assemble the CUPS specific arguments.
387
388 @param printer reference to the printer to print to
389 @type QPrinter
390 @return assembled CUPS arguments
391 @rtype list of str
392 """
393 options = []
394 options.extend(self._optionMedia(printer))
395 options.extend(self._optionDoubleSidedPrinting(printer))
396 options.extend(self._optionPageOrder(printer))
397 options.extend(self._optionCollateCopies(printer))
398 options.extend(self._optionCupsProperties(printer))
399
400 return options
401
402 def _optionMedia(self, printer):
403 """
404 Protected method to assemble the print media arguments.
405
406 @param printer reference to the printer to print to
407 @type QPrinter
408 @return assembled print media arguments
409 @rtype list of str
410 """
411 pageSize = self._mediaPageSize(printer)
412 paperSource = self._mediaPaperSource(printer)
413
414 if pageSize and paperSource:
415 return ["-o", "media={0},{1}".format(pageSize, paperSource)]
416
417 elif pageSize:
418 return ["-o", "media={0}".format(pageSize)]
419
420 elif paperSource:
421 return ["-o", "media={0}".format(paperSource)]
422
423 return []
424
425 def _mediaPageSize(self, printer):
426 """
427 Protected method to get the page size argument.
428
429 @param printer reference to the printer to print to
430 @type QPrinter
431 @return page size argument
432 @rtype str
433 """
434 pageSize = printer.pageSize()
435 if pageSize in self.__paperSizesMap:
436 return self.__paperSizesMap[pageSize]
437 else:
438 return ""
439
440 def _mediaPaperSource(self, printer):
441 """
442 Protected method to get the paper source argument.
443
444 @param printer reference to the printer to print to
445 @type QPrinter
446 @return paper source argument
447 @rtype str
448 """
449 paperSource = printer.paperSource()
450 if paperSource in self.__paperSourcesMap:
451 return self.__paperSourcesMap[paperSource]
452 else:
453 return ""
454
455 def _optionDoubleSidedPrinting(self, printer):
456 """
457 Protected method to assemble the double sided printing arguments.
458
459 @param printer reference to the printer to print to
460 @type QPrinter
461 @return assembled double sided printing arguments
462 @rtype list of str
463 """
464 duplex = printer.duplex()
465
466 if duplex == QPrinter.DuplexNone:
467 return ["-o", "sides=one-sided"]
468 elif duplex == QPrinter.DuplexAuto:
469 if printer.orientation() == QPrinter.Landscape:
470 return ["-o", "sides=two-sided-short-edge"]
471 else:
472 return ["-o", "sides=two-sided-long-edge"]
473 elif duplex == QPrinter.DuplexLongSide:
474 return ["-o", "sides=two-sided-long-edge"]
475 elif duplex == QPrinter.DuplexShortSide:
476 return ["-o", "sides=two-sided-short-edge"]
477 else:
478 return [] # use printer default
479
480 def _optionPageOrder(self, printer):
481 """
482 Protected method to assemble the page order arguments.
483
484 @param printer reference to the printer to print to
485 @type QPrinter
486 @return assembled page order arguments
487 @rtype list of str
488 """
489 if printer.pageOrder() == QPrinter.LastPageFirst:
490 return ["-o", "outputorder=reverse"]
491 else:
492 return ["-o", "outputorder=normal"]
493
494 def _optionCollateCopies(self, printer):
495 """
496 Protected method to assemble the collate copies arguments.
497
498 @param printer reference to the printer to print to
499 @type QPrinter
500 @return assembled collate copies arguments
501 @rtype list of str
502 """
503 if printer.collateCopies():
504 return ["-o", "Collate=True"]
505 else:
506 return ["-o", "Collate=False"]
507
508 def _optionCupsProperties(self, printer):
509 """
510 Protected method to assemble the CUPS properties arguments.
511
512 @param printer reference to the printer to print to
513 @type QPrinter
514 @return assembled CUPS properties arguments
515 @rtype list of str
516 """
517 options = Globals.toList(printer.printEngine().property(
518 QPrintEngine.PrintEnginePropertyKey(0xfe00)))
519
520 cupsOptions = []
521 index = 0
522 while index < len(options):
523 if options[index + 1]:
524 cupsOptions.extend(["-o", "{0}={1}".format(
525 options[index], options[index + 1])])
526 else:
527 cupsOptions.extend(["-o", options[index]])
528 index += 2
529
530 return cupsOptions
531
532
533 def isCupsAvailable():
534 """
535 Static method to test the availability of CUPS.
536
537 @return flag indicating the availability of CUPS
538 @rtype bool
539 """
540 if Globals.isMacPlatform():
541 # OS X/MacOS always have CUPS
542 return True
543 elif Globals.isLinuxPlatform():
544 testPrinter = QPrinter()
545 return testPrinter.supportsMultipleCopies()
546 else:
547 return False
548
549
550 def printFile(printer, fileName,
551 fileDeletePolicy=FilePrinter.ApplicationDeletesFiles,
552 pageSelectPolicy=FilePrinter.ApplicationSelectsPages,
553 pageRange=""):
554 """
555 Static method to print a file.
556
557 Note: Only CUPS and LPR on *nix systems is supported.
558
559 @param printer reference to the printer to print to
560 @type QPrinter
561 @param fileName name (path) of the file to be printed
562 @type str
563 @param fileDeletePolicy policy determining who deletes the file to be
564 printed (application or system)
565 @type int (0 or 1)
566 @param pageSelectPolicy policy determining who selects the pages to be
567 printed (application or system)
568 @type int (0 or 1)
569 @param pageRange string determining the page range(s) to be printed, if
570 SystemSelectsPages was given for pageSelectPolicy and user chose
571 Selection in print dialog
572 @type str
573 """
574 fp = FilePrinter()
575 if fp._doPrintFile(printer, fileName, fileDeletePolicy, pageSelectPolicy,
576 pageRange):
577 _FilePrintJobs.append(fp)

eric ide

mercurial