Installing and configuring the CUPS-PDF virtual printer driver

Some applications, such as LibreOffice, have built-in support for exporting their output to PDF files. For applications without built-in support for creating PDF files there may already be an installed ‘Print to File (PDF)’ driver in your installation that you can use. However, the Print to File (PDF) option does not appear in the list of available printers in all applications. Fortunately, CUPS has a driver named CUPS-PDF which you can install to create a virtual printer that will produce PDF files.

I install the package net-print/cups-pdf and use the CUPS Printer Manager in a browser to set up a virtual printer to ‘print’ PDF files. I use the driver in conjunction with a shell script that calls a utility to display a ‘Save As’ dialogue box so the user can specify the directory and name of the PDF file.

KDialog (KDE/Qt) and Zenity (GTK+) are two well-known dialogue box tools for use in shell scripts (see, for example, the Linux Magazine article Adding graphic elements to your scripts with Zenity and KDialog). However, although I use KDE on my main laptop, I was unable to get KDialog working properly in a shell script launched by the CUPS PDF driver, so I resorted to using Zenity, which I found simple to use and reliable. My shell script using Zenity is listed further down.

Although Zenity has performed this job perfectly for me in all releases of KDE since 2007, it rankled that I could not get KDialog to do the job. However, a couple of Gentoo users were able to use KDialog successfully with the CUPS PDF driver, each with a different approach to the other, but their approaches both consisted of two shell scripts. In contrast, a solution using Zenity uses only one shell script. You can see the KDialog solutions by the two users (Havin_it and sicvolo) in Gentoo Forums thread [TIP] CUPS-PDF “Save As” with kdialog. I was determined to have a single shell script using KDialog, and was able to conflate sicvolo‘s two-script solution. My thanks go to him for his clever code in his two shell scripts, as I would never have worked it out myself. My single shell script using KDialog is listed further down.

First I will explain how to install the CUPS PDF driver package, then how to use the CUPS Printer Manager to install the virtual printer, and finally how to use a shell script with either Zenity or KDialog in order to display a graphical dialogue box prompting you to specify the directory and file name for the PDF file to be created. I use Gentoo Linux, but the procedure will be similar in other distributions (package manager commands excepted).

1.0  Installation of the cups-pdf driver and virtual printer

1.1  Install the cups-pdf package:

# emerge cups-pdf

1.2  Launch a Web browser and enter http://localhost:631/ in the Address bar to display the CUPS Printer Manager.

1.3  Click on ‘Administration’ to call up the Administration page, then click ‘Add Printer’. If prompted, enter the username ‘root’ (without the quotes) and the root user’s password.

1.4  Select ‘CUPS-PDF (Virtual PDF Printer)’ in the list of local printers, and click ‘Continue’.

1.5  Enter a name, decription and location for the virtual printer. For example, I entered ‘Virtual_PDF_Printer’, ‘Virtual PDF Printer’ and ‘Mesh Edge DX’ (without the quotes), respectively. Then click ‘Continue’.

1.6  Select ‘Generic’ in the ‘Make:’ box, and click ‘Continue’.

1.7  Select ‘Generic CUPS-PDF Printer (w/ options) (en)’ in the ‘Model:’ box, and click ‘Add Printer’. The virtual printer should now be available for you to use.

1.8  When you come to print from an application, if you select Virtual_PDF_Printer from the list of available printers the PDF file will be saved in the directory /var/spool/cups-pdf/<your username>/. Note that the directory will be created automatically the first time you ‘print’ to PDF.

1.9  Optionally, you could create a shortcut (Desktop Config File) on your Desktop to open the directory mentioned in Step 1.8 above.

1.10 Optionally, instead of Step 1.9 you could edit the file /etc/cups/cups-pdf.conf as root user to get cups-pdf to ‘print’ PDF documents to your Desktop instead of in /var/spool/cups-pdf/<your username>/, by changing the line:

#Out /var/spool/cups-pdf/${USER}

to:

Out ${HOME}/Desktop


2.0  How to display a dialog box prompting for the directory and file name of the PDF file

If you prefer to be prompted for a file name so that you can save the PDF file wherever you want and give it any name you want, perform the steps below instead of Steps 1.9 or 1.10 above. Either perform the steps in 2.1 (Zenity) or the steps in 2.2 (KDialog). Then perform the steps in 2.3, which apply in both cases.

2.1   Zenity (GTK+)

2.1.1   Install Zenity:

# emerge zenity

2.1.2   Check it is installed:

# eix -I zenity
[I] gnome-extra/zenity
Available versions: 3.12.1 {debug libnotify test +webkit}
Installed versions: 3.12.1(13:49:47 04/12/14)(libnotify webkit -debug -test)
Homepage: https://wiki.gnome.org/Projects/Zenity
Description: Tool to display dialogs from the commandline and shell scripts

2.1.3   Using a text editor, create the following shell script in your home directory and give it a meaningful name, such as cups-pdf_script.sh:

#!/bin/bash
CURRENT_PDF="${1}"
CURRENT_USER="${2}"
DISPLAY=:0.0
export DISPLAY
XAUTHORITY=/home/${CURRENT_USER}/.Xauthority
export XAUTHORITY
PDFNAME=$(zenity --file-selection --save --confirm-overwrite)
mv "$CURRENT_PDF" "$PDFNAME"

2.2   KDialog (KDE/Qt)

2.2.1   Install KDialog:

# emerge kde-base/kdialog

2.2.2   Check it is installed:

# eix -I kde-base/kdialog
[I] kde-base/kdialog
Available versions: (4) 4.12.5(4/4.12) (~)4.14.2(4/4.14) (~)4.14.3(4/4.14)
{aqua debug}
Installed versions: 4.14.3(4)(00:56:36 04/12/14)(-aqua -debug)
Homepage: http://www.kde.org/
Description: KDialog can be used to show nice dialog boxes from shell scripts

2.2.3   Using a text editor, create the following shell script in your home directory and give it a meaningful name such as cups-pdf_script.sh:

#!/bin/bash

get_dbus()
{
if [ -z $1 ]; then
    echo "specify user" >> $HOME/cups-pdf_script.log
    return 1
fi
# Search these processes for the session variable (they are run as the current user and have the DBUS session variable set)
compatiblePrograms=( kdeinit kded4 pulseaudio trackerd )

# Attempt to get a program pid
for index in ${compatiblePrograms[@]}; do
   PID=$(ps -ef | grep $1 | grep ${index} | head -1 | awk '{print $2}')
   if [[ "${PID}" != "" ]]; then
      break
   fi
done
if [[ "${PID}" == "" ]]; then
   echo "Could not detect active login session" >> $HOME/cups-pdf_script.log
   return 1
fi
QUERY_ENVIRON="$(tr '\0' '\n' < /proc/${PID}/environ | grep "DBUS_SESSION_BUS_ADDRESS" | cut -d "=" -f 2-)"
if [[ "${QUERY_ENVIRON}" != "" ]]; then
#   echo ${QUERY_ENVIRON} >> $HOME/cups-pdf_script.log
   return 0
else
   echo "Could not find dbus session ID in user environment." >> $HOME/cups-pdf_script.log
   return 1
fi
}

CURRENT_PDF="$1"
CURRENT_USER="$2"

export DISPLAY=:0
export XAUTHORITY=/home/${CURRENT_USER}/.Xauthority
export HOME=/home/${CURRENT_USER}/
CONFIG_FILE="/home/${CURRENT_USER}/.pdf-writer.conf"
CANCELLED="No"

date > $HOME/cups-pdf_script.log

get_dbus ${CURRENT_USER}
if [ $? -eq 0 ]; then
   export DBUS_SESSION_BUS_ADDRESS=${QUERY_ENVIRON}
   while [ True ]; do
      CURDATE=$(/bin/date +%Y%m%d)
      CURNAME=$(/bin/cat "$CONFIG_FILE")
      BASENAME=$(basename $CURRENT_PDF)
      FILENAME=$(/usr/bin/kdialog --getsavefilename "$CURNAME$CURDATE-" "*.pdf" --title="Save PDF")
      if [ $? -eq 1 ]; then
         CANCELLED="Yes"
         break
      fi
      echo $FILENAME
      if [ ! "$FILENAME" = "" ]; then
         if [ -e "$FILENAME" ]; then
            /usr/bin/kdialog --warningcontinuecancel "File already exists"
            if [ $? -eq 1 ]; then
               continue;
            fi
         fi
         FILENAME=$(echo $FILENAME.pdf | sed -re "s/(\.pdf)+$/.pdf/g")
         break;
      else
         /usr/bin/kdialog --error "You must select a file or hit Cancel."
      fi
      break
   done
   if [ "${CANCELLED}" == "No" ]; then
      /bin/cp "${CURRENT_PDF}" "${FILENAME}"
      okular "${FILENAME}" &
   fi
   /bin/rm "${CURRENT_PDF}"
   echo "No errors encountered." >> $HOME/cups-pdf_script.log
   exit 0
else
   /bin/rm "${CURRENT_PDF}"
   echo "Errors encountered." >> $HOME/cups-pdf_script.log
   exit 1
fi

2.3   Whichever of the above two options (Zenity or KDialog) you chose, do the following:

2.3.1   Make the shell script file executable:

# chmod +x /home/<your username>/cups-pdf_script.sh

2.3.2   Edit the file /etc/cups/cups-pdf.conf, find the line starting with ‘#PostProcessing‘, and change it to:

PostProcessing /home/<your username>/cups-pdf_script.sh

2.3.3   Restart CUPS:

# /etc/init.d/cupsd restart # If your installation uses OpenRC.

# systemctl restart cups.service # If your installation uses systemd.

Now, when you select Virtual_PDF_Printer from your applications’ list of available printers and click ‘Print’, a pop-up window should appear, allowing you to select the target directory for the PDF file and enter a file name of your choice.

About Fitzcarraldo
A Linux user with an interest in all things technical.

7 Responses to Installing and configuring the CUPS-PDF virtual printer driver

  1. CnZhx says:

    Hello Fitzcarraldo,

    Thanks for this amazing blog. I learnt how to send desktop notification using kdialog from system console (such as from crontab job).

    But it looks like KDE Plasma 5 has changed the DBUS_SESSION_BUS_ADDRESS to “unix:path=/run/user/1000/bus”. If I replace your previous programme obtained value with this one. The notification cannot go to the desktop as it did before. Do you have any ideas what other things should also be added/changed?

    • Fitzcarraldo says:

      I found out that D-Bus stores the session address in a file in the directory ~/.dbus/session-bus and the following version of the script cups-pdf_script.sh using KDialog works for me in KDE Plasma 5:

      #!/bin/bash
      
      get_dbus()
      {
        if [ -z $1 ]; then
            echo "specify user" >> $HOME/cups-pdf_script.log
            return 1
        fi
      
        DBUS_SESSION_FILE=$HOME/.dbus/session-bus/$(cat /var/lib/dbus/machine-id)-0
        if [ -e "$DBUS_SESSION_FILE" ]; then
          . "$DBUS_SESSION_FILE"
          export DBUS_SESSION_BUS_ADDRESS
          return 0
        else
          echo "Could not find DBus session file" >> $HOME/cups-pdf_script.log
          return 1
        fi
      }
      
      CURRENT_PDF="$1"
      CURRENT_USER="$2"
      
      export DISPLAY=:0
      export XAUTHORITY=/home/${CURRENT_USER}/.Xauthority
      export HOME=/home/${CURRENT_USER}/
      CONFIG_FILE="/home/${CURRENT_USER}/.pdf-writer.conf"
      CANCELLED="No"
      
      date > $HOME/cups-pdf_script.log
      
      get_dbus ${CURRENT_USER}
      if [ $? -eq 0 ]; then
         while [ True ]; do
            CURDATE=$(/bin/date +%Y%m%d)
            CURNAME=$(/bin/cat "$CONFIG_FILE")
            BASENAME=$(basename $CURRENT_PDF)
            FILENAME=$(/usr/bin/kdialog --getsavefilename "$CURNAME$CURDATE-" "*.pdf" --title="Save PDF")
            if [ $? -eq 1 ]; then
               CANCELLED="Yes"
               break
            fi
            echo $FILENAME
            if [ ! "$FILENAME" = "" ]; then
               if [ -e "$FILENAME" ]; then
                  /usr/bin/kdialog --warningcontinuecancel "File already exists"
                  if [ $? -eq 1 ]; then
                     continue;
                  fi
               fi
               FILENAME=$(echo $FILENAME.pdf | sed -re "s/(\.pdf)+$/.pdf/g")
               break;
            else
               /usr/bin/kdialog --error "You must select a file or hit Cancel."
            fi
            break
         done
         if [ "${CANCELLED}" == "No" ]; then
            /bin/cp "${CURRENT_PDF}" "${FILENAME}"
            okular "${FILENAME}" &
         fi
         /bin/rm "${CURRENT_PDF}"
         echo "No errors encountered." >> $HOME/cups-pdf_script.log
         exit 0
      else
         /bin/rm "${CURRENT_PDF}"
         echo "Errors encountered." >> $HOME/cups-pdf_script.log
         exit 1
      fi
      
      • CnZhx says:

        Brilliant! It looks like the session-id file was changed accordingly, too.

        P.S. Do you happen to know how to make something like this work in cron? I made a cron job for a script like this but for other purpose. The message box does not reach my desktop even with your new method.

      • Bojan says:

        Hi:
        I created cups.sh file as the user, and amended cups-pdf.config by uncomment postprocessing. Unfortunately, printing by kdialog does not work. The log file comprises the following line:
        Could not detect active login session
        Do you have any idea what may go wrong.
        I use openSuse Leap 15.3 and KDE plasma 5.23.2

        • Fitzcarraldo says:

          I don’t use openSUSE, so cannot be of much help. Perhaps $DBUS_SESSION_FILE could be different in your installation. Try finding out what it is in openSUSE. If you don’t know how to do that, try asking for help in the openSUSE forums.

  2. CnZhx says:

    Thank you Fitzcarraldo. The link you provided seems wonderful but I failed to figure out how to use it for display a message.
    Finally, I realised that I could use a script with sleep and run it in user desktop environment instead.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.