Printing/Developer Tools

From KDE Community Wiki

Did you know you could develop, test and debug 98% of CUPS and KDEPrint functions without the need to own or access a real printer?! You only need to set up virtual "Fileprinters". See the details outlined below. "But... but... but I don't own a printer, therefore I can't help debug or improve KDEPrint" from now on is clearly an excuse.

Two backends to create virtual "fileprinters"

Here are 2 shell scripts to be used as "backends" for CUPS, which provide the means to setup such fileprinters:

  • 2file
  • 2dir

2file saves each printjob into a specific, named file. Each new job will overwrite the previous one. Useful if you don't need to keep the files, and don't want to delete each one.

2dir saves each printjob, under a uniq name to a specific directory. Useful if you need to keep the files (for debugging, viewing, comparing in a text editor, etc.).

Usefulness

You may ask: "But there is a 'file:/' device already in CUPS. Why not use that?" Hey, if you know the (undocumented) CUPS 'file:/' device at all, you should also know its very limited scope and its serious limitations, no? For example, it can not be used for printing application/octet-stream mime types (such as may arrive from Windows/Samba print clients). It is also missing the "print to directory" mode implemented by the 2dir script here. And it is not a script or a separate backend file, but a fixed builtin component of CUPS, so you can't hack it. Hence these two little darlings....

Both our backends may be used with any printer driver (PostScript or not) even though the respective device may be not around. The resulting file will be exactly as the one that would go over the wire to the physical printer, having passed through the complete filtering chain the cupsd may invoke for the current job. (Attention: the current version of both backends do not work reliably when combined with banner page printing, or when using multiple files per job.).

Of course, it is best you use a PPD meant for a real PostScript printer, because that will result in a file that you can look at with KGhostview or KPDF (should you need to debug stuff, don't forget to use alternative viewers, such as 'gv' or 'gs -sDEVICE=x11' too). PPDs meant for PCL printers make CUPS invoke a filtering chain that produces PCL -- therefore, to look at the resulting files you need a PCL viewer (such as GhostPCL). Similar with other non-PostScript formats.

How to use

Save the two backend scripts to your /usr/lib/cups/backend/ directory and make them executable. If your distro of choice didn't patch the soul out of CUPS (like $buntu and partially $debian do/did in the past), setting file permissions to 700 should make the backends run as root (only in case you need this for testing -- otherwise 555 is fine).

No need to restart cupsd since 1.2. The backends are utilized by the 'cups-deviced' as soon as needed. You can test that by invoking (as root)

 lpinfo -v

which lists all available backends. Our two new ones should be amongst them. (See also further below for more details about the 'lpinfo' command and 'cups-deviced' ). Or run

 lpinfo -v | grep -E "2dir|2file"

to only see the two.

To install virtual printers using these backends, run (as root, or as a user with CUPS administrative privileges) something like:

 # this will install a printer named "fileprinter", 
 # printing to file "/tmp/kde4print.testfile.prn"
 lpadmin -p fileprinter \
         -v 2file:/tmp/kde4print.testfile.prn \
         -E \
         -L "on my harddisk, each new job overwrites previous one" \
         -D "virtual printer to test KDEPrint" \
         -P /path/to/any/postscriptprinter.ppd
 # this will install a printer named "dirprinter", 
 # printing to directory "/tmp/kde4print_testdir"
 mkdir /tmp/kde4print_testdir
 lpadmin -p dirprinter \
         -v 2dir:/tmp/kde4print_testdir \
         -E \
         -L "on my harddisk, each job saved under a uniq name" \
         -D "virtual printer to test KDEPrint" \
         -P /path/to/any/postscriptprinter.ppd

Of course, once you are familiar with this, you can immediately start to test our beloved, but heavily neglected kaddprinterwizard using these two backends. To get our two new scripts displayed when choosing the printer backend in the wizard, select "Other printer type" on the "Backends" page of the wizard, click "Next" and find them in the list (their names should be appearing underneath the "file" node). Select the one you want, click "Next" again and fill in the URI accordingly. From here you are on your own...

Now continue to play with and fix the KDE Add Printer Wizard, kprinter, kjobviewer and all of KDEPrint to your heart's content... (Don't forget KDE3's faults too, when you look at KDE4).


'2file' CUPS backend script (Bash code)

 ################# "2file" CUPS backend script #############################
 
 #!/bin/bash
 #
 # /usr/lib/cups/backend/2file
 #
 # (c) September 2007  Kurt Pfeifle <[email protected]>
 #                                  <[email protected]>
 #     Network printing consultant Linux/Unix/Windows/Samba/CUPS
 #
 # License: GNU GPLv2 or GPLv3 (your choice)
 # Warranty: None at all; you may need to fix included defects on your own.
 #
 
 backend=${0}
 jobid=${1}
 cupsuser=${2}
 jobtitle=${3}
 jobcopies=${4}
 joboptions=${5}
 jobfile=${6}
 
 # the following messages should appear in /var/log/cups/error_log,
 # depending on what "LogLevel" setting your cupsd.conf carries:
 echo "INFO: backend=${backend}"       1>&2
 echo "INFO: jobid=${jobid}"           1>&2
 echo "INFO: cupsuser=${cupsuser}"     1>&2
 echo "INFO: jobtitle=${jobtitle}"     1>&2
 echo "INFO: jobcopies=${jobcopies}"   1>&2
 echo "INFO: joboptions=${joboptions}" 1>&2
 echo "INFO: jobfile=${jobfile}"       1>&2
 echo "INFO: printtime=${printtime}"   1>&2
 echo "EMERG:  This is a \"emergency\" level log message" 1>&2
 echo "ALERT:  This is a \"alert\" level log message"     1>&2
 echo "CRIT:   This is a \"critical\" level log message"  1>&2
 echo "ERROR:  This is a \"error\" level log message"     1>&2
 echo "WARN:   This is a \"warn\" level log message"      1>&2
 echo "NOTICE: This is a \"notice\" level log message"    1>&2
 echo "INFO:   This is a \"info\" level log message"      1>&2
 echo "INFO:   This is a 2nd \"info\" level log message"  1>&2
 echo "INFO:   This is a 3rd \"info\" level log message"  1>&2
 echo "DEBUG:  This is a \"debug\" level log message"     1>&2
                                                                                                           
 
 # we will read the output filename from the printers $DEVICE_URI environment
 # variable that should look s.th. like "2file:/path/to/a/filename.suffix"
 
 # Now do the real work:
 case ${#} in
       0)
          # this case is for "backend discovery mode"
          echo "file 2file \"KDEPrint Devel Dept.\" \"2file backend to test CUPS and help KDEPrint development\""
          exit 0
          ;;
       5)
            # backend needs to read from stdin if number of arguments is 5
          cat - > ${DEVICE_URI#2file:}
          ;;
       6)
            # backend needs to read from file if number of arguments is 6
          cat ${6} > ${DEVICE_URI#2file:}
          ;;
       1|2|3|4|*)
          # these cases are unsupported
          echo " "
          echo " Usage: 2file job-id user title copies options [file]"
          echo " "
          echo " (Install as CUPS backend in /usr/lib/cups/backend/2file)"
          echo " (Use as 'device URI' like \"2file:/path/to/writeable/jobfile.suffix\" for printer installation.)"
          exit 0
 esac
 
 echo  1>&2
 
 # we reach this line only if we actually "printed something"
 echo "NOTICE: processed Job ${jobid} to file ${DEVICE_URI#2file:}" 1>&2
 echo "NOTICE: End of \"${0}\" run...."                             1>&2
 echo "NOTICE: ---------------------------------------------------" 1>&2
 echo  1>&2
 exit 0
 
 ################# end "2file" #############################################


'2dir' CUPS backend script (Bash code)

 ################# "2dir" CUPS backend script ##############################
 
 #!/bin/bash
 #
 # /usr/lib/cups/backend/2dir
 #
 # (c) September 2007  Kurt Pfeifle <[email protected]>
 #                                  <[email protected]>
 #     Network printing consultant Linux/Unix/Windows/Samba/CUPS
 #
 # License: GNU GPLv2 or GPLv3 (your choice)
 # Warranty: None at all; you may need to fix included defects on your own.
 #
 
 backend=${0}
 jobid=${1}
 cupsuser=${2}
 jobtitle=${3}
 jobcopies=${4}
 joboptions=${5}
 jobfile=${6}
 printtime=$(date +%Y-%b-%d-%H-%M-%S)
 
 # the following messages should appear in /var/log/cups/error_log,
 # depending on what "LogLevel" setting your cupsd.conf carries:
 echo "INFO: backend=${backend}"       1>&2
 echo "INFO: jobid=${jobid}"           1>&2
 echo "INFO: cupsuser=${cupsuser}"     1>&2
 echo "INFO: jobtitle=${jobtitle}"     1>&2
 echo "INFO: jobcopies=${jobcopies}"   1>&2
 echo "INFO: joboptions=${joboptions}" 1>&2
 echo "INFO: jobfile=${jobfile}"       1>&2
 echo "INFO: printtime=${printtime}"   1>&2
 echo "EMERG:  This is a \"emergency\" level log message" 1>&2
 echo "ALERT:  This is a \"alert\" level log message"     1>&2
 echo "CRIT:   This is a \"critical\" level log message"  1>&2
 echo "ERROR:  This is a \"error\" level log message"     1>&2
 echo "WARN:   This is a \"warn\" level log message"      1>&2
 echo "NOTICE: This is a \"notice\" level log message"    1>&2
 echo "INFO:   This is a \"info\" level log message"      1>&2
 echo "INFO:   This is a 2nd \"info\" level log message"  1>&2
 echo "INFO:   This is a 3rd \"info\" level log message"  1>&2
 echo "DEBUG:  This is a \"debug\" level log message"     1>&2
 
 
 # we are free to compose the output filename written by the 2dir backend
 # in whatever way we like... However, we must be careful when using the
 # $jobtitle part -- the job may originate from a web browser and contain
 # slashes and all kinds of illegal or problematic characters. Therefore
 # we prefer to radically convert every "weird" character to an underscore
 # for our current purpose...
 
 sanitized_jobtitle="$(echo ${jobtitle} | tr [[:blank:]:/%\&=+?\\\\#\'\`\´\*] _)"
 
 # the following lines would do (nearly) the same as the above, but slower
 # -- yet better readable (and the above may be in need of some fixing still):
 #sanitized_jobtitle="$(echo ${jobtitle} \
 #                |tr [:blank:] _ \
 #                |tr : _ \
 #                |tr \"\ \" _ \
 #                |tr / _ \
 #                |tr % _ \
 #                |tr \' _ \
 #                |tr \` _ \
 #                |tr \´ _ \
 #                |tr \' _ \
 #                |tr \& _ \
 #                |tr \* _ \
 #                |tr \# _ \
 #                |tr = _ \
 #                |tr + _ \
 #                |tr ? _ \
 #                |tr \\\\ _ )"
 ##               # last line to get rid of "backslashes"...
 
 # now for our final job output name:
 outname=${jobid}_${printtime}_${sanitized_jobtitle}
 # we include the $printtime part to have a uniq name in case the same job
 # is tested with different settings to be kept for comparing. It is also
 # useful for aligning debug efforts to CUPS' error_log entries.
 
 
 # we will read the output directory from the printers $DEVICE_URI environment
 # variable that should look s.th. like "2dir:/path/to/a/directory" and write
 # our printfile as $outname there....
 
 # Now do the real work:
 case ${#} in
       0)
          # this case is for "backend discovery mode"
          echo "file 2dir \"KDEPrint Devel Dept.\" \"2dir backend to test CUPS and help KDEPrint development\""
          exit 0
          ;;
       5)
          if [ ! -e ${DEVICE_URI#2dir:} ]; then
            mkdir -p ${DEVICE_URI#2dir:}
            # You may want to change this to 777 to allow you
            # to periodically delete the generated files:
            chmod 755 ${DEVICE_URI#2dir:}
            # WARNING! WARNING! WARNING! Don't use these file permissions
            # on a production print server! This is for development convenience
            # only! WARNING, security risk!
          fi
 
          # backend needs to read from stdin if number of arguments is 5
          cat - > ${DEVICE_URI#2dir:}/${outname}
 
          # Make sure everyone can read it
          chmod 644 ${DEVICE_URI#2dir:}/${outname}
          # WARNING! WARNING! WARNING! Don't use these file permissions
          # on a production print server! This is for development convenience
          # only! WARNING, security risk!
          ;;
       6)
          if [ ! -e ${DEVICE_URI#2dir:} ]; then
            mkdir -p ${DEVICE_URI#2dir:}
            # You may want to change this to 777 to allow you
            # to periodically delete the generated files:
            chmod 755 ${DEVICE_URI#2dir:}
            # WARNING! WARNING! WARNING! Don't use these file permissions
            # on a production print server! This is for development convenience
            # only! WARNING, security risk!
          fi
 
          # backend needs to read from file if number of arguments is 6
          cat ${6} > ${DEVICE_URI#2dir:}/${outname}
 
          # Make sure everyone can read it
          chmod 644 ${DEVICE_URI#2dir:}/${outname}
          ;;
       1|2|3|4|*)
          # these cases are unsupported
          echo " "
          echo " Usage: 2dir job-id user title copies options [file]"
          echo " "
          echo " (Install as CUPS backend in /usr/lib/cups/backend/2dir)"
          echo " (Use as 'device URI' like \"2dir:/path/to/writeable/directory\" for printer installation.)"
          exit 0
 esac
 
 echo 1>&2
 
 # we reach this line only if we actually "printed something"
 echo "NOTICE: processed Job ${jobid} to file ${DEVICE_URI#2dir:}/${outname}" 1>&2
 echo "NOTICE: End of \"${0}\" run...."                                       1>&2
 echo "NOTICE: ---------------------------------------------------------"     1>&2
 echo 1>&2
 exit 0
 
 ################# end "2dir" ##############################################

Some background info about the 'snmp' backend

Further above we mentioned the "cups-deviced" and introduced the

 lpinfo -v

command. This command may have taken a short while to complete, because the cups-deviced now runs every one of the executable files in the backend directory in "discovery mode" (that is, with zero arguments).

In this mode (which must be supported by each CUPS backend in order to be recognized as one such by CUPS) backends need to report to stdout what they are doing (you'll already have seen their output in "kaddprinterwizard" as well, or you will come to see it if you play some more).

One of the backends that may be there (if not disabled by your distro's CUPS packagers), is called snmp. That one is able to discover available network printers not yet installed on your local system by running an SNMP scan.

(As from CUPS 1.3.0, the backend will only run if it there is a valid snmp.conf in /etc/cups/. One minimal snmp.conf file that should work for many locations is simply:

 Address @LOCAL
 Community public
 DebugLevel 0

See also http://localhost:631/help/ref-snmp-conf.html?TOPIC=References&QUERY= and http://localhost:631/help/network.html?TOPIC=Getting+Started&QUERY=#SNMP as well as 'man snmp.conf' for more details).

Should there be any not-yet-installed network printer on your LAN, the cups-deviced will suggest what it thinks is the best device URI for the printer to use ( 'ipp://nnn.nnn.nnn.nnn' or 'socket://nnn.nnn.nnn.nnn' or 'lpd://nnn.nnn.nnn.nnn' where 'nnn.nnn.nnn.nnn' represents the device's actual IP address.

All results (including the recommended device URIs for each auto-discovered network printer) of this snmp scan should be appearing in the kaddprinterwizard output as well. It is one of the things to test for when helping to get KDEPrint back into shape!