I have a HP printer that can print only on one side of papers. HP driver on Windows has a manual two sided printing feature, that is, whenever I want to print multiple pages on two sides of papers, it prints odd number pages first and then asks me to take the papers out and put it back in the tray, so that it can print even number pages on the other side of the papers. On Linux the driver doesn’t have this feature and I wanted to emulate this process. I also wanted it to act as a virtual printer that can be shared, so that I can start the process with any device on the network.
Script
I searched for similar problems and I found this snippet:
$ lp -o page-set=odd $filename
$ # ... manually flip pages ...
$ lp -o page-set=even $filename
It was not suited for my printer. The printer I have outputs the paper rotated and in reverse order. The first and top paper that goes into the printer, will be flipped and stay at the bottem of the output tray. This script prints the even pages correctly:
$ lp -o page-set=odd $filename
$ # ... manually put the papers back ...
$ lp -o page-set=even -o orientation-requested=6 -o outputorder=reverse $filename
The option orientation-requested=6
rotates the paper 180 degrees.
There was another problem with this scripts.
When I’m printing a document that has odd number of pages, the last paper has only one page on one side and the other side needs to be blank.
I could print the blank page in this case, but I found that swapping two lp
commands fixes the issue and also avoids printing a blank page.
This solution saves printing a single page compared to official HP driver on Windows:
$ lp -o page-set=even -o orientation-requested=6 -o outputorder=reverse $filename
$ # ... manually put the papers back ...
$ lp -o page-set=odd $filename
Virtual printer
The next step was to add this script as a virutal printer. I learned that CUPS has backends, and I could add my own script as another backend. With more search and reading similar solutions I came up with this:
#!/bin/bash
# Place this file at /usr/lib/cups/hp-virtual, chown root:root and chmod 0744.
if [[ $# -eq 0 ]]; then
echo 'file hp-virtual:/ "HP Virtual Printer" "HP Virtual Printer"'
exit 0
fi
JOBID="$1"
USER="$2"
TITLE="$3"
COPIES="$4"
OPTIONS="$5"
filename="$(mktemp /tmp/hp-virtual.XXXXXXXX.pdf)"
cat > "${filename}"
pagecount="$(pdfinfo "${filename}" | awk '/Pages:/ { print $2 }')"
lpargs=""
for opt in ${OPTIONS}; do
lpargs="${lpargs} -o ${opt}"
done
lp -s -d NAME -t "${TITLE}-even" -o page-set=even -o orientation-requested=6 -o outputorder=reverse "${filename}"
# ask to manually put the papers back...
lp -s -d NAME -t "${TITLE}-odd" -o page-set=odd "${filename}"
rm -f "${filename}"
NAME
is the CUPS’ name for my HP printer.
This backend does not respect the $COPIES
argument, as I rarely print documents with multiple copies.
There are also other features that could be interesting to have here, but the core of the process is the script above.
I needed to come up with something for # ask to manually put the papers back...
.
KDialog
I used kdialog for this kind of tasks a lot, so it was appealing to do the same thing here.
The problem is that I am using wayland, and DISPLAY=:0
is not enough, although it may worked fine on X11.
The script below turned out to be useful:
#!/bin/bash
# Place this somewhere (/path/to/env-wrapper) and chmod +x
set -o allexport
XDG_RUNTIME_DIR="/run/user/$(id -u)"
eval "$(systemctl --user show-environment)"
exec "$@"
So now I can write this in my CUPS backend.
sudo -u MYUSER -- /path/to/env-wrapper kdialog --yesno "Press OK when printing one side is finished."
if [[ $? -ne 0 ]]; then
rm -f "${filename}"
exit 0
fi
You can download the whole backend script at my gist or on this blog.
References
-
CUPS admin panel: http://localhost:631/
-
CUPS
lp
options: https://www.cups.org/doc/options.html -
CUPS backend interface, used for specifying the device URI: https://www.cups.org/doc/man-backend.html
-
CUPS backend/filter API: https://www.cups.org/doc/api-filter.html
-
Similer question on manual duplex printing: https://unix.stackexchange.com/questions/150288
-
Similer backend implementation: https://github.com/dentys03/manual_duplex_linux/blob/master/usr/lib/cups/backend/duplex-print
The main difference between this and my implementation is that I do not need a filter, and that I needed to rotate and reverse the document for even pages. It also mentions a bug with
page-set=even
that prints a blank page before printing the document. Although I didn’t have this bug, but I found that printing even pages first in reverse order implicitly solves this problem. The first blank paper will be the last paper after printing the odd pages. -
ArchWiki page on CUPS, used to setup CUPS server: https://wiki.archlinux.org/title/CUPS
-
ArchWiki page on printer sharing: https://wiki.archlinux.org/title/CUPS/Printer_sharing
-
Share printer using
cupsctl
andlpadmin
: https://www.cups.org/doc/sharing.htmlFor some unknown reason I couldn’t enable printer sharing on the admin panel. I had to enable it in the terminal.