Creating Smart Print Queues

This article will help you understand print filters and how to create and install your own personalized filters.
A Simple Print Filter

If we want to modify the print file that is sent to a queue, or perform some other task before printing, we use the output filter printcap keyword, of. The filter program is usually kept in the spool directory for the queue which uses it, such as /var/spool/lpd/lp1. Listing 2 shows a printcap entry that uses a simple filter. The printer port is defined by the lp= field as usual. When we use the of=/var/spool/lpd/filter field, the print daemon pipes the original file through this filter before it is sent to the device set by the lp field. After the lf field, I'm using the sf and sh boolean fields to tell the print daemon to suppress form feeds and suppress the header page for this queue.

Listing 2

So what can we do with our printer filter? Just about anything you could imagine. Listing 3 is a filter for printing plain text on HP printers. When Hewlett-Packard printers are not sent data in their native print language (such as PCL, HPGL or Postscript), they expect MS-DOS line endings consisting of an ASCII carriage return and line feed (newline). If you print a UNIX-style text file, which uses a newline only to mark the end of a line, the second and subsequent lines of your file will not start on the left edge of the paper. Without the carriage return to tell the printer to move to the left edge, you get a stairstep effect. There is no line wrap, so once the text moves off the right edge of the paper, all is lost to the proverbial bit bucket.

Listing 3

This filter uses the awk command to insert a carriage-return and newline at the end of each line of UNIX text. As I mentioned above, the BSD printer daemon pipes the file being printed to standard input of the filter. It then passes the standard output of the filter to the print device specified on the lp= field of the printcap entry.

Getting Sophisticated

The above example works great to fix a common printing problem, but it is good for text files only. If you accidentally send a PostScript or, heaven forbid, a graphics file to this queue, you will be rewarded with lots of useless paper and a scornful system administrator.

Listing 4 is the filter we want to use to solve this problem. First, we specify a log file where we can write debugging information from the script. Next, we capture standard input to a file with the cat command. We can then check this file to determine what kind of file it is using the file command. If it is a text file, we process it as we did in our simple filter above.

Listing 4

If the file is a PostScript file, we run ghostscript to format it for our particular type of non-Postscript Printer. In this case, I have specified a Color Deskjet, which works for my HP Deskjet 660C. Of course, if you should be so lucky as to have a PostScript printer, you would not need this filter at all.

The last branch of our filter lets us print TIFF graphic files. We simply insert the tiff2ps command in front of the ghostscript command. In this way, you can easily add the ability to process other types of files without manually formatting them. You might want to use the full path names to the commands, since the lp daemon will not have the same path that a login shell does.

Getting Smarter

I also wanted my smart print queue to be able to deal with several different printers. You see, I use a laptop for all my e-mail and my personal computing. When I am at home, I use an old Epson-compatible printer for simple print tasks. If I need higher quality output, and my kids let me unplug it from their computer, I connect to the HP Deskjet. When I am at work, I print to an HP Laserjet 4MV PostScript printer on the network.

The change in printers is handled easily by adding information about the type of printer to the filter. To set the type of printer that is available, I added a line in $HOME/.profile to ask me which printer I will be using. This is run once when I first log in after booting up my laptop. The printer type I enter is written to the file /tmp/printer. I use the names that Ghostscript recognizes.

Listing 5 is the filter that uses the printer type information. I added the variable PTYPE to hold the name of the printer I am currently using. The first-level “if” branch checks for the file type, as in the previous filter. Within these branches, a second-level “if” branch checks for the different printers.

Listing 5

The other change in the filter is that instead of writing the output to standard output, I pipe it to another lpr print command. On the lpr command, I specify a print queue for the attached or network printer. This bypasses the lp= line in printcap. For this to work, I created another printcap entry for a print queue called “raw”. The raw printcap entry outputs to the actual printer. To create a raw print queue, just take Listing 1, remove the lp default name, and change the queue name to raw. I use a different raw print queue for the network printer, since it is a PostScript printer. Each raw printer has its own print queue in the printcap file. The raw queue for whichever printer is attached to the parallel port, and “4mvraw” is for the network printer when I'm at work.

Listing 6 is the printcap entry for the above filter and its corresponding raw print queue. One thing that is different in this entry compared to that of a normal printer queue is using /dev/null as the printer device. Since our filter will be handling the output (by printing the filtered output to the raw device), we don't need lpd to send it to any device.

Listing 6

If you have more than one printer available to you, you could use this filter technique to send any of the different types of files to different printers. And if you have a FAX modem, you could even add a “print to FAX” feature to your printing system.