Printing with a Linux System

[Linux]

30th August 2001

Printing under Linux has caused me some grief in the past and I wrote some notes so I could remember what to do. I've cleaned up those notes and put them here. I find them useful, and if anyone else does too, it's a bonus.

If you want more generic information, have a look at www.linuxprinting.org.

[Note 3rd July 2003] I have changed from this method to using CUPS for my printing. See my CUPS Notes.

My Problems

[Linux]

I started with an early Slackware in 1994, and had no problems with printing. I worked for a company that wrote a Unix typesetting system and they had recompiled it for Linux. It was called TopSet and was a very neat system. You could edit with your flavour of editor, and then typeset and print with their typesetting engine. While I worked for the company, they were moving the typesetting system support SGML and it was a very interesting and fun time. I really liked the typesetting system. I ran the Linux version of the typesetting system at home on my Slackware box and it worked until about 1997 or 1998 when Slackware introduced a new print system called lprNG. That's what me memory tells me, but it could be wrong. When I upgraded to the next version of Slackware, typesetting stopped. The new print mechanism would not accept the sort of output coming from TopSet. I spent a lot of time working out why printing had stopped, and in the meantime, I learnt how printing worked. I was greatly helped by a large cheap book called The Linux Bible, which was a printing of a bunch of HOW-TOs and FAQs. There were as an excellent article in there about the basic BSD style print mechanism.

When I learnt what the problem was, I pondered my options and tried a few things and fiddled with Slackware, but I finally decided to move to RedHat 5.2. The print mechanism was much slicker with RedHat and quite easy to configure, with their GUI or by editing the config files. So I continued to print quite happily until 2000, when for various reasons, I switched back from RedHat to Slackware. I found that lprNG was gone and the standard lpr system was back again. By this time I had a network print server and the Slackware print system didn't appear to like it. Probably more to the point, I didn't have the patience to learn a new print system. I wanted something easy like the RedHat system. So I analysed the RedHat system and found that I couldn't copy it directly. Rather than do any work myself, I looked on freshmeat to see if someone else had done something. Sure enough, someone had. Mike Petullo had written print_filter, a simple script that does what the RedHat system did. The initial version used a RedHat utility called rewindstdin, but later versions removed the need for that. I installed it, modified it a bit to suit my tastes, and it works very well. In setting it up, I had to re-learn those earlier lessons about the way printing works, and I wrote fresh notes on it.

Introduction

[Linux]

Printing on most versions of Linux is fairly simple. The print mechanism that I am describing is often called the BSD-style Printing Mechanism. There are four major parts.

  • You use the program lpr to start a printjob. lpr hands the file over to the print daemon lpd.
  • The print daemon lpd will read the the /etc/printcap file on startup. This file specifies the printers and how they are to be handled. lpd will then sit and wait for files from lpr. When it gets a file, it adds it to the queue. It watches the queue and when there's something to be printed, it grabs the file from the queue, ...
  • ... processes it according to /etc/printcap ...
  • ... and eventually dumps it to the bare device that the printer is attached to. On a single printer attached to the parallel port, this is usually /dev/lp0.

lpr

Using lpr is pretty easy. It comes standard with almost every Linux distribution. If everything is set up right, to use it to print something, you just type "lpr filename". If your print mechanism is working properly, you will see your hard disk lights flicker, and then your printer will start printing. It should all happen transparently. If your system is not set up right, then nothing will happen.

lpr has many options. You don't normally need any. But if you want to get fancy, do "man lp" and see what you can do.

lpd

You should never have to worry about lpd. It's installed for you, and it starts running at boot time. If you want to check if it's running, type "ps -ef | grep lpd" and confirm that it's there.

If you want to see where it gets started, you have to look in the startup directories. With Slackware, look in the file /etc/rc.d/rc.M and search for lpd. There's a single command that starts your print daemon.

/etc/printcap for a local printer

[Linux]

This is the file that controls your print setup. Generally, a sample version is created for you when a Linux distribution is installed.

Here is an early version of my /etc/printcap file.

lp|lj:\
  :lp=/dev/lp0:\
  :sd=/var/spool/lpd/lp:\
  :if=/var/spool/lpd/lp/print_filter.sh:\
  :mx#0:\
  :sh:

Basically, it's one printer per line. You can have any number of printers specified in the file. However the one printer per line rule makes it clumsy to read and maintain, so the single line for a printer is usually broken into its various components by escaping each line (using a backslash on the end of each line to indicate that the line continues to the next line). The line must start with the printer name (and aliases), but after that, the components can be in any order. The components take this form: ":key=value:". The key is a two character abbreviation.

The first line specifies the name of the printer. The default name is lp. If you use lpr without specifying a printer, then it assumes you want the printer called "lp". So you should always specify a printer in /etc/printcap called "lp". It means less typing. You can give aliases for lp if you want. Here, I've used the alias "lj", a tiny abbreviation of Hewlett-Packard LaserJet 6L. If I wanted, I could include other aliases on the same line by adding them after lj and separating them with the pipe symbol (|). Example: lp|lj|hplj6l. So I could print to this printer using the aliases, as in "lpr -Plj" or "lpr -Phplj6l". These would produce exactly the same result as "lpr", with my /etc/printcap. I normally have no need to include other aliases. I don't even need or use one alias. I just include it because I can.

A lot of different things can be specified. If you do "man printcap", you'll see a heap of components that can be specified. I use a very simple set of components, just enough to do the job for me. If I was setting up a print server for a big corporation with many printers and many users, I would use a lot more of the options. Have a read through the man page and see what's available.

lp - line printer

This specifies the hardware device where the printer is located. "lp=/dev/lp0". Check the /dev directory and make sure the device lp has been created for you, and it has the right permissions. If you want to test that the printer is attached and the connection works, copy a file to the device.

  • If your printer is a Postscript printer, get a Postscript file and copy it to the device with "cp filename.ps /dev/lp0". Your printer should produce a print of the Postscript file.
  • If it's not a Postscript printer, it will probably accept text, so take a text file and copy it to the device with "cp filename.txt /dev/lp0". Your printer should print it and do a real raw job of it.

sd - spool directory

You have to specify where the print files get queued or spooled. You do this with the "sd" component. Often, they are kept in /var/spool/lpd. At various times, I have had more than one printer attached, so I have kept them in different directories. I usually use a directory off /var/spool/lpd, and to keep things straight, I make it the name of the printer. So I keep the queue files for print lp in /var/spool/lpd/lp. If I had another printer attached called lexcol (Lexmark Colour for example), then I would set up a directory called /var/spool/lpd/lexcol.

Once you set this up in printcap, you have to ensure that the directories exist and have the right ownership and permissions. Because lpd runs as root, the directories should be owned by root and have rwxr-xr-x permission.

Once printing starts, three files will be created in the directory you specified.

  • .seq contains the print job numbers
  • lock is a lock file on that directory
  • status contains a description of the result of the last print job

From now on, all the action for your printer will take place in the directory specified with "sd".

Each time lpr gives a file to lpd, several files are written to the spool directory. One of these files is the contents of the file to be printed, another contains the details of the print request. If your print mechanism stops and you have a large number of files in the queue and you want to get rid of them, you can always come here, check out which files are which and manually remove them. Check the filenames and check the contents before you start deleting everything.

if - input filter

This specifies your input filter. The input filter is a magical device that transforms the file you want printed into output suitable for the printer. The input filter does a very complex job and is the hardest thing to set up. In my example, "if=/var/spool/lpd/lp/print_filter.sh", I am storing a shell script called print_filter.sh in my spool directory and using it as the input filter. I notice that lately, some input filters are no longer stored in the spool directory, but in more common places like /usr/local/bin. Either way is fine.

The input filter is used like this:

  • lpd passes the file to be printed through STDIN to the input filter
  • the input filter processes the file
  • the input filter passes a new file out through STDOUT
  • the new file gets copied directly to the print device (eg /dev/lp0).

If you want to test if the whole print mechanism is working, you can do it manually, step by step. I do this when I am debugging my input filter.

  • Log in as root for these tests.
  • Change directory to your spool directory or wherever you store your input filter. It's easier to work there. With my printcap, I would "cd /var/spool/lpd/lp".
  • Get a file that you want to test. (Example: test.txt)
  • Pass that file into the input filter (in my printcap it's print_filter.sh) through STDIN. This is best done by:
    • ./print_filter.sh < test.txt
  • The modified version of the file will be pumped out via STDOUT, so will come out on the screen. If you aren't using a Postscript printer, this will pump a bunch of non-printing characters to your screen which will ruin your display. This isn't much use, so you should save it to a file so you can examine it and do stuff with it later. So instead of the previous command, use this:
    • ./print_filter.sh < test.txt > look
  • Have a look at your saved output in the file "look". If you're setup for a Postscript printer, then you can read the Postscript language output. If you're setup for non-Postscript, you'll see a whole bunch of stuff. I use Hewlett-Packards, so what I see is the raw PCL language commands. Make sure you get some valid output. If the file "look" is empty, then your input filter is broken.
  • Assume you got output. You want to check that output with the printer and see if the printer recognises it. Copy the file to the device specified with you "lp" component. "cp look /dev/lp0". Then check the printer. Your file of Postscript or junk should have turned into a nice printout of your original file. If it didn't, then you have a problem with your input filter.
  • What I did in the example above, "./print_filter.sh < test.txt > look" followed by "cp look /dev/lp0" is exactly what lpd does when you use lpr to print a file. You just acted as if you were lpd. If you can do the steps manually and get the desired printout, then your print mechanism is working properly. If it didn't work, then you should have some error messages that will help you find your problem.
  • The general cause of problems is permissions or a bad input filter. Check your permissions on the spool directory (rwxr-xr-x), the input filter (rwxr-xr-x - most important to have execute permissions), and the output device (/dev/lp0 should have at least rw-rw----).

The input filter is the most important part of your print setup. It converts input files into a format like Postscript or PCL so your printer can understand it and know how to print it. You can also use it to do pretty printing of text, or special effects on images, and a wide range of other things. See below for a description of what I use, and some variants.

mx - maximum file size

This is used to control paper wastage. It's best used for non-home setups, or to control accidental printer mistakes. You can specify 0, as I do, to allow files of any size to be printed. Or you can specify a number. The number represents how many BUFSIZ blocks will be allowed to be printed. If the input file size is greater than this size, it will be thrown away. I don't know how big BUFSIZ is. I don't know what the default is. I specify 0 to allow unlimited file sizes, and that suits me fine.

sh - suppress header

If you omit this component, each print job will get an extra page at the front with details of the print job. This is probably nice in a large multi-person environment, but at home it just wastes paper. I use ":sh:" to suppress the header.

/etc/printcap for a remote printer

[Linux]

I use a Hewlett-Packard JetDirect EX Plus3 network print server. This is a small device that attaches to the network and has an IP address. I can have up to 3 printers attached to this device, although at this stage, I only have one printer attached. Anyone on my network can print to the printer. Sure, I could attach the printer to my computer and people could still print from my printer, but only while my computer is turned on. The print server is always on.

To access the print server, I use this /etc/printcap:

lp|lj:\
  :lp=:\
  :rm=netprinter:\
  :rp=raw:\
  :if=/var/spool/lpd/lp/print_filter.sh:\
  :sd=/var/spool/lpd/lp:\
  :mx#0:\
  :sh:

You'll notice that most of the components are the same. if, sd, mx and sh are still the same. lp is empty. And I use two new components.

rm - remote printer

Here I specify the name of the remote printer. In my /etc/hosts file, I have this line:

192.168.30.10  netprinter

My print server has IP address 192.168.30.10. So print jobs are not sent to my local device of /dev/lp0, they are sent to the IP address 192.168.30.10, and I trust that something is there at that IP address to grab the file and do something with it. And it is.

rp - remote printer name

This is used by my print server. I can have three printers attached. So I have one IP address for the three printers, and this field, the remote printer name, is what determines which printer to use. My print server accepts these names - raw (raw1), text (text1), raw2, text2, raw3, text3. Basically, there are two modes of printing. If I specify "raw", it pumps the data straight to the printer without any modification, assuming it to be already PCL or Postscript. If I specify "text", it will add a carriage return to each line before sending it to the printer. I use "raw" mode.

The Input Filter

[Linux]

This is the core of your printing system. It converts input files of one format and converts them to a format suitable for your printer. It can be a compiled program, a Perl script, or a shell script. These days it's mostly a shell script.

Many years ago when I was experimenting with Linux printing, I wrote a Perl script that accepted text files with tags, and produced output that was suitable for a Hewlett-Packard LaserJet 5L printer. It basically threw a few PCL commands around the text. It's Perl 4, and written when I was new to Perl, so it's a bit rough. Here's that old Perl program.

As you can see, it fulfills the requirements of a basic input filter. It accepts input from STDIN, modifies it, and spits the result out through STDOUT. It worked back then, and did a pleasant job of preparing text for me. Nowadays, my needs are greater.

I use a shell script now that uses a number of utilities to modify the input file. Here's a copy of the shell script I use now.

Have a look at the file. It works like this:

  • It uses the "file" command to check the type of file coming in through STDIN.
  • Then it uses a RedHat program called rewindstdin to wind back the file at STDIN after "file" has read a few bytes of it.
  • It then does different things depending on what type of file has been sent to it via STDIN.
  • If it's a text file, the commonest thing I print, it uses the utility a2ps to accept the file through STDIN, pretty print it, turn it into Postscript, and pump it out to STDOUT.
  • It pipes the output from a2ps into Ghostscript (gs) which converts the Postscript into my device (ljet4 which is the closest thing available to my Laserjet 6L) and pumps the output from that to STDOUT.

That's a very simple script that handles a few input formats. If you look at the input filter on the average RedHat or Mandrake system, you'll see that they handle many more formats and do much more processing. They also use a lot more environment variables for the options, whereas I have hardcoded many of them into the script. This script suits my purposes. It handles everything I use now, including output from Netscape, Mozilla, StarOffice, and Gimp. It works. If something stops working, or I change my hardware, I will change my script. I do have plans for it. Eventually, I will move it to /usr/local/bin, and include a print_filter.conf in /etc. These are changes that the original author, Mike Petullo, has made. See the latest version of his script at print_filter. He has also done away with the dependency on RedHat's rewindstdin, by dumping the file to /tmp, using "file" on it there, then feeding the file in through STDIN to a2ps. His approach is now much better than the one I am using, and eventually, when the need takes me, I shall follow his lead.

Summary

[Linux]

BSD style printing with Linux is a long chain of commands. Every step of the way can be made simple, or can be made very complex, depending on your needs. When you understand the full flow of data, you can easily debug your printing when you are setting it up. Once it's set up, you can leave it alone for a long time. If you do customise your setup, make sure you back the files up.