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