Select Git revision
cups-pdf-postprocess.py
cups-pdf-postprocess.py 7.57 KiB
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
XXX
Author: Radek Hušek
"""
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
import subprocess as sp
import ast
import os
CONFIG_FILE = "/etc/cups/cups-pdf-postprocess.conf"
def exec_pipeline(pipeline, inp, out = None, err_callback = None):
def make_proc(cmd):
p = QProcess()
p.setProgram(cmd[0])
p.setArguments(cmd[1:])
return p
def readChunk(cb, proc, channel):
def x():
proc.setReadChannel(channel)
cb(str(proc.readAll(), encoding='utf-8'))
return x
procs = [ make_proc(cmd) for cmd in pipeline ]
for a, b in zip(procs, procs[1:]):
a.setStandardOutputProcess(b)
procs[0].setStandardInputFile(inp)
if out is None:
if err_callback is not None:
procs[-1].readyReadStandardOutput.connect(
readChunk(err_callback, procs[-1], QProcess.StandardOutput))
else:
procs[-1].setStandardOutputFile(out)
if err_callback is not None:
for p in procs:
p.readyReadStandardError.connect(
readChunk(err_callback, p, QProcess.StandardError))
err_callback("< '%s' \\\n" % inp)
err_callback(" |\n".join(("'%s'" % "' '".join(cmd)) for cmd in pipeline))
err_callback("\n")
for p in procs:
p.start(QProcess.Unbuffered | QProcess.ReadWrite)
for p in procs:
while not p.waitForFinished(30):
QApplication.instance().processEvents()
class CPP(QWidget):
def __init__(self, config, pdf, user):
super().__init__()
self._pdf = pdf
self._config = config
self._defaults = self._config.get("defaults", {})
self.init_printers()
self.init_gui()
self.show()
def init_printers(self):
self._save_as_file = object()
self._printers = self._config.get("printers", [])
self._printers.append(("Save as ...", self._save_as_file))
for l in sp.check_output(["lpstat", "-a"]).decode("UTF-8").split("\n"):
p = l.split(" ")[0]
if p == "":
continue
self._printers.append((p, ["lp", "-d", p]))
def init_gui(self):
vbox = QVBoxLayout()
vbox.addStretch(1)
o = QLabel("Printing %s" % (self._pdf,), self)
o.setMinimumWidth(400)
o.setMaximumWidth(800)
o.setWordWrap(True)
vbox.addWidget(o)
def margin_spinbox(name, layout, margin_id):
hbox = QHBoxLayout()
hbox.addWidget(QLabel(name))
s = QSpinBox(self)
s.setMinimum(0)
s.setMaximum(100)
s.setSingleStep(1)
s.setValue(self._defaults.get("crop_margins", [20, 20, 20, 20])[margin_id])
s.setEnabled(False)
hbox.addWidget(s)
layout.addLayout(hbox)
return s
hbox = QHBoxLayout()
self._crop = QCheckBox('C&rop', self)
hbox.addWidget(self._crop)
_vbox = QVBoxLayout()
_vbox.addWidget(QLabel("Margins [mm]"))
self._crop_margin = (
margin_spinbox("Left", _vbox, 0),
margin_spinbox("Top", _vbox, 1),
margin_spinbox("Right", _vbox, 2),
margin_spinbox("Bottom", _vbox, 3),
)
hbox.addLayout(_vbox)
vbox.addLayout(hbox)
def _crop_changed(checked):
for m in self._crop_margin:
m.setEnabled(checked)
self._crop.clicked[bool].connect(_crop_changed)
if self._defaults.get("crop", False):
self._crop.setChecked(True)
_crop_changed(True)
hbox = QHBoxLayout()
self._compact = QCheckBox('Co&mpact', self)
self._compact_copies = QSpinBox(self)
self._compact_copies.setMinimum(1)
self._compact_copies.setMaximum(1000)
self._compact_copies.setSingleStep(1)
self._compact_copies.setValue(self._defaults.get("copies", 1))
self._compact_copies.setEnabled(False)
def _compact_changed(enabled):
self._pdfbook.setEnabled(not enabled)
self._compact_copies.setEnabled(enabled)
if enabled:
self._duplex.setCurrentIndex(1)
self._pdfbook.setChecked(False)
_pdfbook_changed(False)
self._duplex.setEnabled(not enabled)
self._compact.clicked[bool].connect(_compact_changed)
if self._defaults.get("compact", False):
self._compact.setChecked(True)
_compact_changed(True)
hbox.addWidget(self._compact)
hbox.addWidget(self._compact_copies)
vbox.addLayout(hbox)
hbox = QHBoxLayout()
hbox.addWidget(QLabel("Duplex:"))
self._duplex = QComboBox(self)
self._duplex.addItem("None", 'duplex=one-sided')
self._duplex.addItem("Long Edge", "duplex=two-sided-long-edge")
self._duplex.addItem("Short Side", "duplex=two-sided-long-edge")
self._duplex.setCurrentIndex(1)
self._pdfbook = QCheckBox('Use pdf&book', self)
self._staple = QCheckBox('&Staple', self)
self._staple.setEnabled(False)
def _pdfbook_changed(enabled):
self._staple.setEnabled(enabled)
self._duplex.setEnabled(not enabled)
if enabled:
self._duplex.setCurrentIndex(1)
self._pdfbook.clicked[bool].connect(_pdfbook_changed)
if self._defaults.get("pdfbook", False):
self._pdfbook.setChecked(True)
_pdfbook_changed(True)
if self._defaults.get("staple", False):
self._staple.setChecked(True)
hbox.addWidget(self._duplex)
vbox.addLayout(hbox)
vbox.addWidget(self._pdfbook)
vbox.addWidget(self._staple)
o = QComboBox(self)
for (p, cmd) in self._printers:
o.addItem(p, cmd)
vbox.addWidget(o)
self._printer = o
hbox = QHBoxLayout()
b_p = QPushButton("&Print", self)
b_p.clicked.connect(self.print)
hbox.addWidget(b_p)
self._button_print = b_p
b_c = QPushButton("&Cancel", self)
b_c.clicked.connect(self.close)
hbox.addWidget(b_c)
self._button_cancel = b_c
vbox.addLayout(hbox)
self._logger = QTextEdit()
self._logger.setReadOnly(True)
vbox.addWidget(self._logger)
self.setLayout(vbox)
self.setWindowTitle('CUPS pdf printer postprocessor')
def print(self):
lp = QProcess()
self._button_print.setEnabled(False)
log = []
pipeline = []
def logMsg(msg):
log.append(msg)
self._logger.setPlainText("".join(log))
MM_TO_BP = 2.83465
if self._crop.isChecked():
margin = " ".join([ "%d" % (MM_TO_BP * m.value()) for m in self._crop_margin ])
pipeline.append([ "%s/pdfcrop_wrapper.sh" % DIR,
"--margin", margin, "-", "/dev/stdout" ])
if self._pdfbook.isChecked():
pipeline.append([ "pdfbook", "/dev/stdin", "-o", "/dev/stdout" ])
if self._compact.isChecked():
pipeline.append([ "%s/print_compact.sh" % DIR,
"%i" % self._compact_copies.value() ])
if self._printer.currentData() is self._save_as_file:
f = QFileDialog.getSaveFileName(self, 'Save as ...')
if not f[0]:
self.close()
pipeline.append([ "dd", "of=%s" % (f[0],) ])
else:
cmd = self._printer.currentData().copy()
cmd += [ "-o", self._duplex.currentData() ]
if self._staple.isChecked():
cmd += [ "-o", "XRFold=BiFoldStaple" ]
cmd.append("-")
pipeline.append(cmd)
exec_pipeline(pipeline, self._pdf, err_callback=logMsg)
self._button_cancel.setText("&Close")
DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
if __name__ == '__main__':
args = sys.argv
if args[1] == '-c':
CONFIG_FILE = args[2]
args = args[2:]
else:
CONFIG_FILE = os.getenv('CUPS_PDF_POSTPROCESS_CONFIG', CONFIG_FILE)
with open(CONFIG_FILE) as config:
CONFIG = ast.literal_eval("{\n%s\n}" % config.read())
# env must be fixed before we create Qt stuff
for var, val in CONFIG.get("enviroment", {}).items():
if val is None:
del os.environ[var]
else:
os.environ[var] = val
app = QApplication(args[2:])
ex = CPP(CONFIG, args[1], args[2])
sys.exit(app.exec_())