Next Previous Contents

4. Print Spooling Tutorial

A print spooler is a program that accepts print jobs (which are usually one or more files) from a program or network interface, stores them in a spool queue, and then sends them to a printer or another print spooler. Usually there are facilities to submit jobs, check on the current job status, remove jobs from spool queues, and perform administrative functions such as starting or stopping printing.

A print spooler is a client/server application. The client programs are used to submit jobs to the print spooler program which performs the actual printing operations. In order to carry out these operations, the server may need to use other programs to convert print job files into a format acceptable to a printer, or perform various accounting or administrative functions.

4.1 Overview

+---------+    +-----+    +-----+     +--------+    +---------+
| program | -> | lpr | -> | lpd |  -> | filter | -> | printer |
+---------+    +-----+  * +-----+     +--------+    +---------+
                  *    *     |
               printcap      V
                          +-----+     +--------+    +---------+
                          | lpd |  -> | filter | -> | printer |
                          +-----+     +--------+    +---------+

                           Figure 1

Figure 1 shows the flow of data between the individual components of the LPRng print spooling system. A program (or user) will use the lpr program to send a file to the lpd server over a TCP/IP connection. The lpd server will store the file temporarily in a spool queue directory. The information needed by the lpr and lpd programs to carry out this activity is stored in the printcap (usually called the /etc/printcap) database file.

The lpd server sorts the queue entries and determines the print order. It will select a job to be printed, open a connection to the printer, and then use a filter program to convert the file contents into a format suitable for the printer. If the file does not need conversion, then the lpd server will send the file directly to the printer.

The lpd server can also forward jobs to another print server over a network connection, optionally sending them through a filter as well. The destination server can in turn forward the job or send it to a printer.

The protocol or commands used to do this job forward and transfer are specified by RFC1179. This protocol specifies how the lpr client program sends a job to the lpd server, as well as how the lpd server forwards jobs to another server. In addition to job submission, RFC1179 specifies commands to obtain queue status, to remove jobs from the queue, and to start and stop print queues.

4.2 Sample Printcap Entry

As described in the Print Spooling Overview, the information in the printcap database is used control printing operations. While there is no RFC specifying its format or content, there is a strong de facto standard for its format. For a complete description of the printcap database see Using the Printcap Database. For a list of all of the printcap and configuration options see Index To All The Configuration and Printcap Options.

Here is a sample printcap suitable for use by the LPRng clients:

# LPRng
lp:lp=psqueue@printserver.astart.com
# Classical BSD
lp:rp=psqueue:rm=printserver.astart.com

The printcap information tells the client programs that jobs for the lp printer are sent to the psqueue print queue queue on host printerserver.astart.com. The classical BSD printcap is also shown. The rp value is the remote printer (actually a spool queue) and the rm value is the server. When both lp and the rp/rm are present the lp value has precedence.

On the printserver the following is a sample printcap entry suitable for the lpd server:

psqueue:server
  :lp=/dev/lp0
  :sd=/var/spool/lpd/psqueue
  :if=/usr/local/libexec/filters/ifhp

The sd (spool queue directory) entry specifies the directory where print jobs received from the lpr program will be placed. The lp value is the output device the lpd server will use to print the job. The if (input file filter) is a program that translates the input file to a format compatible with the printer. If there is not need for a conversion filter then this entry is left out.

4.3 Print Server Configuration

The previous sections have given a very high level view of printing operations. In order to do printing the following programs and information must be established:

  1. The printer itself, and the interface to the printer.
  2. Client programs for use by users or other programs to send jobs to the print server and perform administrative functions.
  3. A server program that runs on a host that accepts jobs for printing.
  4. Printcap information to control the printing operations.
  5. Filters that convert print jobs into formats compatible with printers.
  6. System facilities such as spool queues or storage areas for jobs.

The following sections will cover each of these topics in turn. The initial sections assume that most users are setting up printers on small systems and require an extremely simple print capability. Later sections explore the various configurations that can be used to support large networks of print spoolers as would be found in large academic institutions or businesses.

4.4 Tutorial Configuration

In order to simplify operation and not waste lots of paper, we will use a very simple printcap entry during this tutorial. During the tutorial we will also show how to modify the printcap to add additional capabilities. You should run these tests as a nonprivileged (non-ROOT) user except where specifically noted.

By default, the LPRng system uses the printcap file in /etc/printcap or /usr/local/etc/printcap. Save the existing file and then create a new printcap file as shown below.

#  -- obtain root permissions.  You will need to entry the password
su
cd /etc    OR    cd /usr/local/etc
mv printcap printcap.orig
vi printcap
#  -- put the following lines into the printcap file:
lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
lp2:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp2
#  -- save the printcap file and set permissions so programs
#     can read it
chmod 644 printcap

First, we use the su command to get SuperUser (ROOT) privileges. We then save the original printcap file, and create a new simple one.

This printcap file has two entries: lp and lp2. Each print queue on the server needs a spool file to hold print jobs, jobs, and the :sd value specifies its location. The %P value is replaced with the name of the printer when it is used.

The force_localhost is a very powerful option used by LPRng to provide classical BSD or lightweight operation. In classical BSD operation each host has an lpd print spooler running on the local host (we use localhost in this manual for simplicity). Files were copied to spool directories on the localhost and then then print spooler would send them to the destination, which could be another print spooler. This meant that each localhost machine had to have a print spooler and spool queue directory structure. Management of this becomes very difficult in large organizations.

The LPRng lightweight operation allows the client programs to communicate directly with an lpd server that is not running on the localhost. While this would appear to be the most desirable default mode of operation, it turns out in practice that all existing UNIX documentation for print spooler configuration assumes the classical BSD configuration. When the behavior of LPRng is different new users become extremely frustrated. In this tutorial we will show how to use both modes and the advantages and disadvantages of both modes.

The print device (:lp=) will be a file so that we can see what is happening during these tests (and also to save trees).

#  -- create the dummy printing devices
cp /dev/null /var/tmp/lp
cp /dev/null /var/tmp/lp2
chown daemon /var/tmp/lp /var/tmp/lp2
chgrp daemon /var/tmp/lp /var/tmp/lp2
#  we want to read and write the dummy device
chmod 666  /var/tmp/lp /var/tmp/lp2
echo hi >/tmp/hi
echo there >/tmp/there

Next we create the files that will act as our printing devices. We give them world read and write permissions so we can observe their contents. We also create a couple of files containing some information to print.

#  we modify the lpd.perms to allow an ordinary user to control
#  the print queues
mv lpd.perms lpd.perms.orig
vi lpd.perms
# put the following into the lpd.perms file:
DEFAULT ACCEPT

The next set of commands creates a permissions database that will allow all actions by default. This is useful for testing, but dangerous in a working environment.

#  -- checkpc create directories and files for the print queue
#     as well as make sure permissions are correct
checkpc -f -V
# -- output will contain information similar to:
#  Checking printer 'lp'
#   Checking directory: '/var/spool/lp'
#     directory '/var'
#     directory '/var/spool'
#     directory '/var/spool/lp'
#  Warning -   changing ownership '/var/spool/lp' to 1/1
#    checking 'control.lp' file
#    checking 'status.lp' file
#    checking 'status' file
#    cleaning 'status' file, 0K bytes: no truncation
#    checking 'log' file
#    cleaning 'log' file, 0K bytes: no truncation
#    checking 'acct' file
#    cleaning 'acct' file, 0K bytes: no truncation
#  Checking printer 'lp2'
#   Checking directory: '/var/spool/lp2'
#     directory '/var'
#   ....

#  -- signal the lpd server to use the new configuration
lpc reread

#  -- if no server was running you can start the server
lpd

#  -- do the next steps as ordinary user
#     we first exit from su
exit
#  -- check to see if the server is active and the queue is
#     present
lpq
#  example of output:
#  Printer: lp@h4
#    Queue: no printable jobs in queue
#  -- check to see that the reread command is accepted
lpc reread

The checkpc performs consistency checks on the printcap file and spool queue entries. The checkpc -f (fix) option will change permissions and create directories and can only be executed by ROOT. The checkpc has other functions as well - you can view printcap information, see default configuration values, and remove junk files from spool queues with it.

The lpc reread command sends a request to the LPD server to reread the configuration and printcap information. The lpd command is added as insurance in case your lpd server is not running. The exit command restores ordinary user privileges, and the lpq command is used to check that the server is running. Finally, we check to see that the lpc reread command is accepted from an ordinary user.

4.5 Restoring Original Configuration

To restore the original configuration, you simply need to restore the original printcap and lpd.perms file and then restart the lpd server.

<tscreen>
<verb>
#  -- obtain root permissions.  You will need to entry the password
su
cd /etc    OR    cd /usr/local/etc
mv printcap.orig printcap
mv lpd.perms.orig lpd.perms
checkpc -f
lpc reread
rm -rf /var/spool/lpd/lp /var/spool/lpd/lp2
rm -f /var/tmp/lp /var/tmp/lp2
exit

4.6 Printing a File and Checking Status

Try the following commands. The commands appear after the prompt, and sample output that you might see is shown.

h4: {168} % lpr -V /tmp/hi
Version LPRng-3.6.14
sending job 'papowell@h4+238' to lp@localhost
connecting to 'localhost', attempt 1
connected to 'localhost'
requesting printer lp@localhost
sending control file 'cfA238h4.private' to lp@localhost
completed sending 'cfA238h4.private' to lp@localhost
sending data file 'dfA238h4.private' to lp@localhost
completed sending 'dfA238h4.private' to lp@localhost
done job 'papowell@h4+238' transfer to lp@localhost

The lpr -V (Verbose) option causes LPR to print status output. As you can see from the above lines, it first tries to connect to the lpd server on host localhost, then sends a print prequest (which is accepted), then sends a control file containing information about the job and a data file or files which are copies of the files to be printed.

If you check the /var/tmp/lp file and you will find that a copy of /tmp/hi has been written to it. By default, the lpd print spooler acts as a store and forward system, accepting files to be printed, holding them in the print queue, and then forwarding them to the destination system or output device.

You can use the lpq command to view the status of the print job.

h4: {169} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+238' removed at 09:39:03.256

If you want to see more status information, use lpq -l, lpq -ll, or even lpq -L. The -L provides alL the status.

h4: {170} % lpq -l
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256
h4: {171} % lpq -ll
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108
 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256
h4: {172} % lpq -L
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: subserver pid 8240 starting at 09:39:03.105
 Status: accounting at start at 09:39:03.105
 Status: opening device '/var/tmp/lp' at 09:39:03.105
 Status: printing job 'papowell@h4+238' at 09:39:03.106
 Status: no banner at 09:39:03.107
 Status: printing data file 'dfA238h4.private', size 3 at 09:39:03.107
 Status: printing done 'papowell@h4+238' at 09:39:03.107
 Status: accounting at end at 09:39:03.108
 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108
 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256

There are different status formats available as well. The lpq -s (summary) produces a single line of status per spool queue, while the lpq -v (verbose) produces output that is very suitable for processing with programs such as Perl or awk:

h4: {173} % lpq -s
lp@h4  0 jobs
h4: {174} % lpq -v
Printer: lp@h4
 Printing: no
 Aborted: no
 Spooling: no
 Queue: no printable jobs in queue
 SPOOLCONTROL=
 Status: subserver pid 8240 starting at 09:39:03.105
 Status: accounting at start at 09:39:03.105
 Status: opening device '/var/tmp/lp' at 09:39:03.105
 Status: printing job 'papowell@h4+238' at 09:39:03.106
 Status: no banner at 09:39:03.107
 Status: printing data file 'dfA238h4.private', size 3 at 09:39:03.107
 Status: printing done 'papowell@h4+238' at 09:39:03.107
 Status: accounting at end at 09:39:03.108
 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108
 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256

If you check the /var/tmp/lp file and you will find that a copy of /tmp/hi has been written to it. By default, the lpd print spooler acts as a store and forward system, accepting files to be printed, holding them in the print queue, and then forwarding them to the destination system or output device.

4.7 Selecting the Print Queue

In the previous section we used the default print queue. How does LPRng determine what print queue to use? First, you can explicitly specify the printer using the lpq -Pprintqueue option and the lpq -a or lpq -Pall to select all print queues:

h4: {160} % lpq -Plp
Printer: lp@h4
 Queue: no printable jobs in queue
h4: {161} % lpq -Plp2
Printer: lp2@h4
 Queue: no printable jobs in queue
h4: {162} % lpq -a
Printer: lp@h4
 Queue: no printable jobs in queue
Printer: lp2@h4
 Queue: no printable jobs in queue

You can combine the lpq -a with the lpq -s option for a summary listing:

h4: {162} % lpq -a
Printer: lp@h4
 Queue: no printable jobs in queue
Printer: lp2@h4
 Queue: no printable jobs in queue
h4: {163} % lpq -s -a
lp@h4  0 jobs
lp2@h4  0 jobs

There is another way to explicitly specify the printqueues listed by lpq -a; see the ALL Printcap Entry for details.

Users can set their default printer by using the PRINTER (highest priority), LPDEST (next), and NGPRINTER (lowest priority), environment variables. For example:

h4: {164} % setenv PRINTER lp2
h4: {165} % lpq
Printer: lp2@h4
 Queue: no printable jobs in queue
h4: {164} % unsetenv PRINTER
h4: {165} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue

Finally, if you do not use the -P printqueue or have an environment variable set, then the default printer chosen by the system administrator will be used. Please see Printer Name and Server IP Address for details.

4.8 Controlling the Print Queue

You can control the input and output functions of the print queue. The lpc enable and lpc disable commands control spooling to the print queue and the lpc stop and lpc start commands control printing (or transfers) from the print queue. There is also a lpc status command that displays administrative status for a print queue.

Let's look at the status displayed when we use these commands:

h4: {30} % lpc disable
Printer: lp@h4
lp@h4.private: disabled
h4: {31} % lpq
Printer: lp@h4  (spooling disabled)
 Queue: no printable jobs in queue
h4: {32} % lpc enable
Printer: lp@h4
lp@h4.private: enabled
h4: {33} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue
h4: {34} % lpc stop
Printer: lp@h4
lp@h4.private: stopped
h4: {35} % lpq
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
h4: {36} % lpc start
Printer: lp@h4
lp@h4.private: started
h4: {37} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue
h4: {38} % lpc status
 Printer           Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4               enabled  enabled    0    none    none

Let's see what happens when we print to a stopped queue:

h4: {79} % lpc stop
Printer: lp@h4
lp@h4.private: stopped
h4: {80} % lpr /tmp/hi
h4: {81} % lpr /tmp/hi /tmp/there
h4: {82} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Rank   Owner/ID                  Class Job Files                 Size Time
1      papowell@h4+17920            A 17920 /tmp/hi                 3 18:14:22
2      papowell@h4+17922            A 17922 /tmp/hi,/tmp/there      9 18:14:30
h4: {83} % lpc status
 Printer           Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4               disable  enabled    2    none    none

The status shows that we have two jobs spooled. The Rank field shows the order, the Owner/ID shows the unique job ID that is assigned to the job and the Class field is the job class (this may be changed with the lpr -C class option). The Job field shows the job number assigned to this job in this particular spool queue. While the ID value never changes as a job moves through the LPRng system, the job number is specific to a particular spool queue and may change if a job is forwarded to another spool queue that has a job with the same job number. The Size field is the total number of printable bytes in the job, and the Time field shows the timestamp associated with the job.

The lpq -s output is very succinct, and does not show the state of the spool queue:

h4: {51} % lpq -s
lp@h4  2 jobs

Now let's start the print queue and watch what happens.

h4: {83} % lpc start
Printer: lp@h4
lp@h4.private: started
h4: {84} % lpq
Printer: lp@h4
 Queue: 2 printable jobs
 Server: pid 17928 active
 Unspooler: pid 17929 active
 Status: opening device '/var/tmp/lp' at 18:14:43.921
 Rank   Owner/ID                  Class Job Files                 Size Time
active papowell@h4+17920            A 17920 /tmp/hi                 3 18:14:22
2      papowell@h4+17922            A 17922 /tmp/hi,/tmp/there      9 18:14:30
h4: {85} % lpq -ll
Printer: lp@h4
 Queue: 2 printable jobs
 Server: pid 17928 active
 Unspooler: pid 17929 active
 Status: printing job 'papowell@h4+17920' at 18:14:43.921
 Status: no banner at 18:14:43.921
 Status: printing data file 'dfA017920h4.private', size 57 at 18:14:43.922
 Rank   Owner/ID                  Class Job Files                 Size Time
active papowell@h4+17920            A 17920 /tmp/hi                 3 18:14:22
2      papowell@h4+17922            A 17922 /tmp/hi,/tmp/there      9 18:14:30

The Rank value of the first job has been changed to active and there is new Status information. If we use lpq -ll we can see the times that the various print operations are carried out, and details of their success or failure.

We can also use the lpq command to see the status of a particular job. We can select jobs by the user name, the ID, or the job number. For example:

h4: {88} % lpc stop
Printer: lp@h4
lp@h4.private: stopped
h4: {89} % echo hi |lpr
h4: {90} % echo there | lpr
h4: {91} % echo test |lpr
h4: {92} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID                  Class Job Files                 Size Time
1      papowell@h4+17959            A 17959 (stdin)                  3 18:23:24
2      papowell@h4+17962            A 17962 (stdin)                  6 18:23:30
3      papowell@h4+17970            A 17970 (stdin)                  5 18:23:35
h4: {93} % lpq 17970
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID                  Class Job Files                 Size Time
3      papowell@h4+17970            A 17970 (stdin)                  5 18:23:35
h4: {94} % lpq papowell
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID                  Class Job Files                 Size Time
1      papowell@h4+17959            A 17959 (stdin)                  3 18:23:24
2      papowell@h4+17962            A 17962 (stdin)                  6 18:23:30
3      papowell@h4+17970            A 17970 (stdin)                  5 18:23:35
h4: {94} % lpq -s 17970
lp@h4  1 jobs
h4: {95} % lpq -s papowell
lp@h4  3 jobs
h4: {96} % lpq -s nobody
lp@h4  0 jobs

Just as we used the lpq -Pqueuename to select a specific print queue, and the lpq -a or lpq -Pall to select all queues:

h4: {167} % lpc -a stop
Printer: lp@h4
lp@h4.private: stopped
Printer: lp2@h4
lp2@h4.private: stopped
h4: {168} % lpc -Pall start
Printer: lp@h4
lp@h4.private: started
Printer: lp2@h4
lp2@h4.private: started

Finally, you can use the lpc command in interactive mode:

h4: {170} % lpc
lpc>status
 Printer           Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4               enabled  enabled    0    none    none
lpc>status all
 Printer           Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4               enabled  enabled    0    none    none
lp2@h4              enabled  enabled    0    none    none
lpc>stop lp
Printer: lp@h4
lp@h4.private: stopped
lpc>start lp
Printer: lp@h4
lp@h4.private: started
lpc>quit

In the interactive or extended command mode, command syntax has the form operation [printqueue] operands. This syntax can also be used on command lines, but is less common:

h4: {171} % lpc stop all
Printer: lp@h4
lp@h4.private: stopped
Printer: lp2@h4
lp2@h4.private: stopped

4.9 Job Removal

Occasionally we print a file and then change our mind and want to cancel the job. The lprm command allows us to do this.

h4: {126} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID                  Class Job Files                 Size Time
1      papowell@h4+17959            A 17959 (stdin)                  3 18:23:24
2      papowell@h4+17962            A 17962 (stdin)                  6 18:23:30
3      papowell@h4+17970            A 17970 (stdin)                  5 18:23:35
h4: {127} % lprm
Printer lp@h4:
  checking perms 'papowell@h4+17959'
  dequeued 'papowell@h4+17959'
h4: {128} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID                  Class Job Files                 Size Time
1      papowell@h4+17962            A 17962 (stdin)                  6 18:23:30
2      papowell@h4+17970            A 17970 (stdin)                  5 18:23:35
h4: {129} % lprm 17970
Printer lp@h4:
  checking perms 'papowell@h4+17970'
  dequeued 'papowell@h4+17970'
h4: {130} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID                  Class Job Files                 Size Time
1      papowell@h4+17962            A 17962 (stdin)                  6 18:23:30

By default, the lprm command removes the first job in the queue that the user has permission to remove. Also, as shown in the example, you can remove a job by specifying the job ID or the job number. If you specify a user name, you remove all of the user's jobs. This can be dangerous:

h4: {137} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID                  Class Job Files                 Size Time
1      papowell@h4+17962            A 17962 (stdin)                  6 18:23:30
2      papowell@h4+18499            A 18499 /tmp/hi                  3 18:56:00
3      papowell@h4+18501            A 18501 /tmp/there               6 18:56:02
h4: {138} % lprm papowell
Printer lp@h4:
  checking perms 'papowell@h4+17962'
  dequeued 'papowell@h4+17962'
  checking perms 'papowell@h4+18499'
  dequeued 'papowell@h4+18499'
  checking perms 'papowell@h4+18501'
  dequeued 'papowell@h4+18501'
h4: {139} % lpq
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+17922' removed at 18:15:13.981

The special user all matches all jobs in a print queue. Clearly you should be careful not to specify lprm all by accident. Even more dangerous is the following command:

h4: {139} % lprm -a all

As you might surmise, this removes all print jobs in all queues, which is an excellent way to purge print queues of all jobs.

4.10 Basic Filter Configuration

A printer usually understands one or more Print Job Languages. Files sent to this printer must be in one of these languages and have the appropriate job format, control characters, or other information. The most common Print Job Languages are PostScript and PCL.

In order for a printer to reliably print a job, it needs to be reset to a known configuration. This is usually done by sending it a special start of job and end of job command. These commands differ from printer to printer, and even depend on the type of print job language that the print job is in. Some vintage line printers also have a set of proprietary escape sequences that are used to set up margins, form size, and other printing characteristics. Usually a setup string of these escape sequences needs to be sent to the printer before the file can be printed.

In order to handle these problems, the LPRng system uses a filter program to provide the set of escape sequences or carry out the initialization required for a printer. The files in a print job are assigned a lower case letter format using the lpr format options; the default format is f. The l (literal or binary) format is used to indicate that the file should be passed directly to the printer, or have at the most a minimal amount of processing. See Print Job Formats for more information about formats and their use with filters.

We will set up a very simple filter and use it to demonstrate how the lpd spooler uses it. First, set up the /var/tmp/testf file as shown below.

#!/bin/sh
# /var/tmp/testf - test filter for LPRng
PATH=/bin:/usr/bin
echo TESTF $0 "$@" >&2
while expr "$1" : '-.*' >/dev/null ;  do
    n=` expr $1 : '-\(.\).*' `;
    v=` expr $1 : '-.\(.*\)' `;
    shift;
    if [ "$n" != 'c' -a -z "$v" ] ; then
        v=$1;
        shift;
    fi
    eval "$n='$v'";
done
echo ENV
printenv
echo LEADER
/bin/cat
echo TRAILER
exit 0

Let us carefully examine the script line by line. The first couple of lines are standard boilerplate. You should always set the PATH value in a filter script or use full pathnames. This is a good practice as it ensures that only the specified directories will be searched for commands.

The next line echos the arguments to file descriptor 2 (STDERR); by convention the filter STDERR output is used for error messages or important status information. We will soon see how this information is displayed by the LPRng software.

The while loop is used to extract the option flags and their values. All of these but the c flag have an argument. Finally we echo LEADER to STDOUT, then copy STDIN to STDOUT, and then echo TRAILER to STDOUT. We then exit with a zero result code.

Now execute the commands below to test the script:

h4: {50} % chmod 755 /var/tmp/testf
h4: {51} % echo hi |/var/tmp/testf -a1
TESTF /var/tmp/testf -a1
LEADER
hi
TRAILER

Let us put this filter into one of our printcap entries. Edit the lp printcap entry so it has the following form, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server as we did in previous exercises.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  :if=/var/tmp/testf

Execute the following commands to print our /tmp/hi test file file and see what changes have occurred in the printing process:

h4: {47} % cp /dev/null /var/tmp/lp
h4: {48} % lpr /tmp/hi
h4: {49} % lpq -llll
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: lp@h4.private: job 'papowell@h4+26593' printed at 21:37:21.312
 Status: job 'papowell@h4+26593' removed at 21:37:21.323
 Status: subserver pid 26683 starting at 21:39:21.908
 Status: accounting at start at 21:39:21.908
 Status: opening device '/var/tmp/lp' at 21:39:21.909
 Status: printing job 'papowell@h4+26681' at 21:39:21.909
 Status: no banner at 21:39:21.909
 Status: printing data file 'dfA026681h4.private', size 3, IF filter 'testf' at
21:39:21.909
 Status: IF filter msg - 'TESTF /var/tmp/testf -Apapowell@h4+26681 -CA -D2000-04
-11-21:39:21.877 -Ff -Hh4.private -J/tmp/hi -Lpapowell -Plp -Qlp -aacct -b3 -d/v
ar/tmp/LPD/lp -edfA026681h4.private -f/tmp/hi -hh4.private -j026681 -kcfA026681h
4.private -l66 -npapowell -sstatus -t2000-04-11-21:39:21.000 -w80 -x0 -y0 acct'
at 21:39:21.914
 Status: IF filter finished at 21:39:22.070
 Status: printing done 'papowell@h4+26681' at 21:39:22.070
 Status: accounting at end at 21:39:22.070
 Status: finished 'papowell@h4+26681', status 'JSUCC' at 21:39:22.070
 Status: subserver pid 26683 exit status 'JSUCC' at 21:39:22.072
 Status: lp@h4.private: job 'papowell@h4+26681' printed at 21:39:22.072
 Status: job 'papowell@h4+26681' removed at 21:39:22.085

The cp command will clear out the /var/tmp/lp file we are using as a dummy output device. Next we use lpr to print the /tmp/hi file and then use lpq -llll to see the status information. If we look at the status, we see that the line containing IF filter msg is the output that testf wrote to STDERR. The lpd server captures filter STDERR messages and puts it them in the spool queue status file. As we see from the message, lpd passes a large number of command line options to our filter. These options and their meanings are discussed in detail in Filter Command Line Flags, but for now we will ignore them.

Sometimes we want to pass only a small subset of these options, or provide them in a specific manner. Modify the printcap entry to have the following form:

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  :if= -$ /var/tmp/testf '$P' $0P -X$-P ${lp} G$%3a

If you redo the previous commands to print our test file and examine the status, you will discover that the options now are:

 Status: IF filter msg - 'TESTF /var/tmp/testf -Plp -P lp -Xlp \
    -Ylp /var/tmp/lp G:' at 01:20:21.560

The -$ suppresses appending the default options to the end of the filter command line. You can add specific options using the $X format; if the option has a non-null value then it will be expanded in the following format:

Option    Value Expansion
$X        -X<value>
$0X       -X <value>
$-X          <value>
${name}   printcap option value
$%XX      character corresponding to 0xXX

You can group options and other values using single or double quotes. These are used only for grouping and will be removed when the command is executed.

The ${name} format is very handy for passing a filter an option value which is set in the printcap. For example, you may decide to create a new printcap option called form, which will set the form type to be used with this spool queue. You can pass it to the filter which will then generate the necessary control codes for the printer to set the form. For example:

Example 1:
lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  :if=-$ /var/tmp/testf -F${form},
  :form=payroll
....
 Status: IF filter msg - 'TESTF /var/tmp/testf -Fpayroll, ' at 01:20:21.560
....

Example 2:
lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  :if=-$ /var/tmp/testf -F '${form}'
  :form=
....
 Status: IF filter msg - 'TESTF /var/tmp/testf -F, ' at 01:20:21.560
....

In the second example the form printcap option has a null value, which is expanded to a empty string. Notice that we have added a comma at the end of the parameter; this will protect the parsing of command line options from failure if the form value is null. Clearly the testf filter must be ready to deal with a comma at the end of the -F parameter.

Now lets look at what is in the output file:

h4: {50} % cat /var/tmp/lp
ENV
LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib
HOME=/home/daemon
PRINTCAP_ENTRY=lp
 :force_localhost
 :if=/var/tmp/testf
 :lp=/var/tmp/lp
 :sd=/var/tmp/LPD/lp
USER=daemon
PS1=$ 
OPTIND=1
PS2=> 
SPOOL_DIR=/var/tmp/LPD/lp
LOGNAME=daemon
CONTROL=Hh4.private
 Ppapowell
 J/tmp/hi
 CA
 Lpapowell
 Apapowell@h4+105
 D2000-04-12-15:27:26.662
 Qlp
 N/tmp/hi
 fdfA000105h4.private
 UdfA000105h4.private
PATH=/bin:/usr/bin:/usr/local/bin
SHELL=/bin/sh
LOGDIR=/home/daemon
PRINTER=lp

LEADER
hi
TRAILER

The lpd server sets the PRINTER, PRINTCAP_ENTRY, and CONTROL environment variables to the printer name, printcap entry, and the control file for the print job. This information is very useful to filters that must make decisions based on values passed to the print server in the control file, and which can also use parameters in the printcap entry to control their actions. We will see how the ifhp filter makes use of this in a later section.

4.11 The Jaggies - LF to CR-LF Conversion With lpf

When printing to vintage hard copy devices or to printers that support a text mode, many UNIX users discover that their output suffers from a case of the jaggies.

Input file:

  This is
  a nice day

Output:

  This is
         a nice day

UNIX systems terminate lines with a single NL (new line) character. This causes the printer to move down one line on the printing page but does not change its horizontal position and print the next character at the left margin. This is done by using the CR (carriage return) character. You need to convert the single NL to a CR-LF combination and the lpf filter supplied with LPRng does this.

First, locate the lpf filter. It is usually in /usr/local/libexec/filters directory, but this may vary from installation to installation. You can find it by using the command:

find / -type f -name lpf -print

We will first see what the output is like without lpf, and then see what it does. Modify the lp printcap entry as shown below and then use lpc restart to restart the lpd server.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp

Print a file and view the output using the following commands. If you do not have the od (octal dump) program, try using hexdump or some other appropriate program that displays the numerical contents of the file.

h4: {134} % cp /dev/null /var/tmp/lp
h4: {135} % lpr /tmp/hi
h4: {136} % od -bc /var/tmp/lp
0000000  150 151 012
           h   i  \n
0000003

Now we will use the lpf filter. Modify the printcap as shown below and use lpc lpd to restart lpd.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  # modify the path to lpf appropriately
  :if=/usr/local/libexec/filters/lpf

Now reprint the file:

h4: {141} % cp /dev/null /var/tmp/lp
h4: {142} % lpr /tmp/hi
h4: {143} % od -bc /var/tmp/lp
od -bc /var/tmp/lp
0000000  150 151 015 012
           h   i  \r  \n
0000004

As you see, lpf changes the LF to a CR-LF sequence.

4.12 Introducing the ifhp filter

The companion ifhp print filter is used with the LPRng to provide hardware level support for PostScript, PCL, text, and other printers. It provides diagnostic and error information as well as accounting information. Obtain the latest or stable version of the ifhp filter source code from a LPRng FTP Site. Normally, ifhp is installed together with the LPRng software.

The PostScript and PCL printer job languages are supported by most printer manufacturers. However, in order to have a job printed correctly the following steps must be taken.

The ifhp filter uses the ifhp.conf database file to determine the actions and commands appropriate for various models of printers. See the ifhp documentation for details about the format and contents of this file. The default printer used by the ifhp filter is the HP LaserJet 4M Plus, which supports PostScript, PCL, and PJL. These defaults will also support a very wide variety of printers that support both PostScript and PCL.

Now we will use the ifhp filter. Find the path to the ifhp filter using the find command as we did in the previous exercise. Modify the printcap as shown below and use lpc lpd to restart lpd.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  # modify the path to ifhp appropriately
  :if=/usr/local/libexec/filters/ifhp

Now reprint the file, and then display /var/tmp/lp using a text editor such as vi or emacs that shows control characters:

h4: {141} % cp /dev/null /var/tmp/lp
h4: {142} % lpr /tmp/hi
h4: {143} % vi /var/tmp/lp
^[%-12345X@PJL
@PJL JOB NAME = "PID 405" DISPLAY = "papowell"
@PJL RDYMSG DISPLAY = "papowell"
@PJL USTATUSOFF
@PJL USTATUS JOB = ON
@PJL USTATUS DEVICE = ON
@PJL USTATUS PAGE = ON
@PJL USTATUS TIMED = 10
@PJL ENTER LANGUAGE = PCL
^]E^]&^]&k2G^]&s0C^]&l0O^]9^](s0P^](s10.00H^](s4099Thi
^]E^]%-12345X@PJL
@PJL RDYMSG DISPLAY = "papowell"
@PJL EOJ NAME = "PID 405"
@PJL USTATUSOFF
@PJL USTATUS JOB = ON
@PJL USTATUS DEVICE = ON
@PJL USTATUS PAGE = ON
@PJL USTATUS TIMED = 10
@PJL RDYMSG DISPLAY = ""
^[%-12345X

The output now contains all of the control sequences and setup codes needed to print a text file on the printer. For details on the exact meaning of each of these entries, see the PCL and PJL documentation.

4.13 PostScript Printer and the ifhp filter

There are some printers that only support PostScript. We will show how to configure the ifhp filter to recognize this. First, create the /tmp/one.ps PostScript file:

%!PS-Adobe-3.0
%% one page (i.e. - a page with a 1 on it)
/Courier
findfont 200 scalefont setfont
72 300 moveto
(1) show
showpage

This PostScript file starts with the standard PostScript header - %! or the acceptible alternative \004%!. If a PostScript file does not start with either of these two character sequences, the printer may behave in unexpected ways. The ifhp filter will check to make sure that the file starts with these sequences.

Modify the printcap as shown below and use lpc lpd to restart lpd.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  # modify the path to ifhp appropriately
  :ifhp=model=ps
  :if=/usr/local/libexec/filters/ifhp

Now print the file /tmp/one.ps, and then display the output in /var/tmp/lp using a text editor such as vi or emacs that shows control characters:

h4: {141} % cp /dev/null /var/tmp/lp
h4: {142} % lpr /tmp/one.ps
h4: {143} % vi /var/tmp/lp
^D%!
%!PS-Adobe-3.0
%% one page (i.e. - a page with a 1 on it)
%%/Times-Roman
/Courier
findfont 200 scalefont setfont
72 300 moveto
(1) show
showpage
^D

As you can see, the PostScript ^D (Control-D or \004) End of Job characters have been prefixed and appended to the file. This will ensure that the job will be handled properly by the printer.

4.14 PCL Printer and the ifhp filter

While PostScript is the most widely support Print Job Language, there are large number of printers that support only PCL. The ifhp filter has support for a PCL only printer.

Modify the printcap as shown below and use lpc lpd to restart lpd.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  # modify the path to ifhp appropriately
  :ifhp=model=pclonly
  :if=/usr/local/libexec/filters/ifhp

Now print the file /tmp/hi, and then display the output in /var/tmp/lp using a text editor such as vi or emacs that shows control characters:

h4: {141} % cp /dev/null /var/tmp/lp
h4: {142} % lpr /tmp/hi
h4: {143} % vi /var/tmp/lp
^]E^]&^]&k2G^]&s0C^]&l0O^]9^](s0P^](s10.00H^](s4099Thi
^]E

The output now contains the necessary PCL start of job, format setup, and end of job sequences needed to print on a PCL printer.

4.15 Using GhostScript for Format Conversion

A major cost in a low end printer printer is the microprocessor and memory which converts PCL or PostScript to a raster image. Most low end printers have the rasterization done on a users computer by a print driver program or utility, and then the raster image is transferred to the printer. The raster image is usually in a proprietary format.

If you have a PostScript file and want to print it on one of these printers then you will need to use GhostScript or a similar program to do the conversion. The following example shows one way to use GhostScript with LPRng and is presented for instructional purposes. It is not recommended method; please consult the ifhp documentation for a more robust method that handles text files and other non-PostScript files correctly.

Suppose that we have an HP DeskJet 660C printer and want to print PostScript files. Enter the following command to see what devices your version of GhostScript is configured for.

h4: {349} % gs --help
GNU Ghostscript 5.10 (1998-12-17)
Copyright (C) 1997 Aladdin Enterprises, Menlo Park, CA.  All rights reserved.
Usage: gs [switches] [file1.ps file2.ps ...]
Most frequently used switches: (you can use # in place of =)
 -dNOPAUSE           no pause after page   | -q       `quiet', fewer messages
 -g<width>x<height>  page size in pixels   | -r<res>  pixels/inch resolution
 -sDEVICE=<devname>  select device         | -dBATCH  exit after last file
 -sOutputFile=<file> select output file: - for stdout, |command for pipe,
                                         embed %d or %ld for page #
Input formats: PostScript PostScriptLevel1 PostScriptLevel2 PDF
Available devices:
   x11 x11alpha x11cmyk x11mono sxlcrt ap3250 appledmp bj10e bj200 bjc600
   bjc800 cdeskjet cdjcolor cdjmono cdj500 cdj550 cp50 declj250 deskjet
   djet500c dnj650c epson eps9mid eps9high epsonc ibmpro imagen iwhi iwlo
   iwlq jetp3852 laserjet la50 la70 la75 la75plus lbp8 lips3 ln03 lj250
   ljet2p ljet3 ljet3d ljet4 lj4dith ljetplus lp2563 m8510 necp6 oce9050
   oki182 paintjet pj pjetxl pjxl pjxl300 r4081 sj48 st800 stcolor t4693d2
   t4693d4 t4693d8 tek4696 xes dfaxhigh dfaxlow faxg3 faxg32d faxg4 tiffcrle
   tiffg3 tiffg32d tiffg4 bmpmono bmp16 bmp256 bmp16m cgmmono cgm8 cgm24 cif
   mgrmono mgrgray2 mgrgray4 mgrgray8 mgr4 mgr8 pcxmono pcxgray pcx16 pcx256
   pcx24b psmono pbm pbmraw pgm pgmraw pgnm pgnmraw pnm pnmraw ppm ppmraw
   sgirgb tifflzw tiffpack tiff12nc tiff24nc bit bitrgb bitcmyk pngmono
   pnggray png16 png256 png16m pdfwrite nullpage
Search path:
   . : /usr/share/ghostscript/5.10 :
   /usr/share/ghostscript/fonts
For more information, see /usr/share/ghostscript/5.10/doc/use.txt.
Report bugs to ghost@aladdin.com; use the form in bug-form.txt.

Next we consult the GhostScript printer support documentation at http://www.cs.wisc.edu/~ghost/printer.html or the documentation shipped with the GhostScript distribution and find that the cd550 driver supports a wide range of printers:

cdj550 
  HP DeskJet 550C (3.53) 
  HP DeskJet 560C (3.53) 
  HP DeskJet 600 (5.10) black (1bit/pixel) and colour (32bit/pixel) 
  HP DeskJet 660C (3.53) 
  HP DeskJet 660C (5.10) 
  HP DeskJet 682C (4.01) Use gamma=0.3 
  HP DeskJet 683C (3.33, 4.03) 
  HP DeskJet 693C (4.03) 24bit/pixel Is this a CMY or a CMYK printer?
    Probably CMYK in which case 32bit/pixel can be used. 
  HP DeskJet 694C (5.03) Works in colour. 690C, 692C, 693C and 694C
     are all software variations. 
  HP DeskJet 695C (5.50) Works in colour. 
  HP DeskJet 850 (3.53) 300dpi CMYK (32bit/pixel) See also cdj850. 
  HP DeskJet 870Cse (4.03) (16 or 32 bits/pixel) Use ljet4 device for B/W.
     See also cdj850. 
  HP DeskJet 895Cxi (5.03, 5.50) Does not work with cdj850. 
  HP DeskJet 970 (5.50) Some pages contain small black boxes,
     lines or missing characters. 
  HP OfficeJet 590 (5.50) 
  Olivetti jp450 (5.50) 
  Xerox XJ6C (5.50) 
  May work with other HP Colour DeskJet printers that use a CMYK ink cartridge.
  Any printer that works with this device will probably work in black and white
    with djet500 and cdjmono. 

Create the /tmp/testgs file with the following contents:

#!/bin/sh
# /tmp/testgs - set the path to gs (GhostScript) appropriately
exec /usr/bin/gs -q -dSAFER -dBATCH -sDEVICE=cdj550 -sOutputFile=- -

Run the following commands to convert the /tmp/one.ps file and view the output with a text editor:

h4: {351} % /tmp/testgs </tmp/one.ps >/tmp/out.img
h4: {352} % vi /tmp/out.img
^[*rbC^[*t300R^[&l2aolE^[*o1d2Q....

Modify the lp printcap as shown below, and use lpc reread to restart the server.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  :if=/tmp/testgs

Print the file /tmp/one.ps, and then display the output in /var/tmp/lp using a text editor such as vi or emacs that shows control characters:

h4: {141} % cp /dev/null /var/tmp/lp
h4: {142} % lpr /tmp/one.ps
h4: {143} % vi /var/tmp/lp
^[*rbC^[*t300R^[&l2aolE^[*o1d2Q....

4.16 Using Printcap Options In Filter Scripts

In the previous section we saw how to set up a filter shell script so that we could convert PostScript files. We can modify this script to extract information from the PRINTCAP environment variable, and make our script much more powerful.

Modify the /tmp/testgs script from the previous example so that it contains:

#!/bin/sh
# /tmp/testgs - set the path to gs (GhostScript) appropriately
device=`echo $PRINTCAP_ENTRY \
  |/usr/bin/sed -n 's/.*:device=\(.*\)/\1/p' `
if [ "X$device" = "X" ] ; then device=epson; fi
# print the device value for status information
echo DEVICE $device >&2
exec /usr/bin/gs -q -dSAFER -dBATCH -sDEVICE=$device -sOutputFile=- -

Modify the lp printcap as shown below, and use lpc reread to restart the server.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  :if=/tmp/testgs

Print the file /tmp/one.ps, display the lpq status as shown below, and then display the output in /var/tmp/lp using a text editor such as vi or emacs that shows control characters:

h4: {146} % cp /dev/null /var/tmp/lp
h4: {147} % lpr /tmp/one.ps
h4: {148} % lpq
 ....
 Status: IF filter msg - 'DEVICE cdj550' at 20:31:34.386

h4: {143} % vi /var/tmp/lp
^[*rbC^[*t300R^[&l2aolE^[*o1d2Q....

Using this technique you can use one filter script to handle a wide variety of printers. This is essentially the method used by the ifhp filter to determine the printer type.

4.17 GhostScript and ifhp

While the previous method is useful when only PostScript files are being printed, it is not very robust. The ifhp filter provides the ghostscript printer configuration which can be used as shown below. The ifhp filter will invoke GhostScript with the indicated options and handle errors and setup in an appropriate manner. The a2ps program is used to do text to PostScript conversion in this configuration.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  :ifhp=model=ghostscript,device=epson,resolution=-r240x72
  # gs ... -sDEVICE=$device $resolution ...
  :if=/usr/local/libexec/filters/ifhp

Modify the lp printcap as shown below, and use lpc reread to restart the server.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/var/tmp/lp
  :ifhp=model=ghostscript,device=cdj550
  :if=/usr/local/libexec/filters/ifhp

Print the file /tmp/one.ps, display the lpq status as shown below, and then display the output in /var/tmp/lp using a text editor such as vi or emacs that shows control characters:

h4: {146} % cp /dev/null /var/tmp/lp
h4: {147} % lpr /tmp/one.ps
h4: {148} % lpq
 ....
 Filter_status: initial job type 'POSTSCRIPT' at 03:50:38.141
 Filter_status: job type 'raw', converter '/usr/bin/gs
   -dSAFER -dBATCH -q -sDEVICE=cdj550 -sOutputFile=- - ' at 03:50:38.141
 Filter_status: started converter '/usr/bin/gs
   -dSAFER -dBATCH -q -sDEVICE=cdj550 -sOutputFile=- - ' at 03:50:38.146
 Filter_status: converter done, output 1251 bytes at 03:50:38.790

h4: {143} % vi /var/tmp/lp
^[*rbC^[*t300R^[&l2aolE^[*o1d2Q....

Next, print the file /tmp/hi, display the lpq status as shown below, and then display the output in /var/tmp/lp using a text editor such as vi or emacs that shows control characters:

h4: {146} % cp /dev/null /var/tmp/lp
h4: {147} % lpr /tmp/hi
h4: {148} % lpq
 ....
 Filter_status: job type 'raw', converter
  '/usr/bin/a2ps -q -B -1 -M Letter --borders=no -o-
  | /usr/bin/gs -dSAFER -dBATCH -q
    -sDEVICE=cdj550 -sOutputFile=- - ' at 03:52:12.423
 Filter_status: started converter
  '/usr/bin/a2ps -q -B -1 -M Letter --borders=no -o-
  | /usr/bin/gs -dSAFER -dBATCH -q
    -sDEVICE=cdj550 -sOutputFile=- - ' at 03:52:12.426
 Filter_status: converter done, output 183 bytes at 03:52:13.206


h4: {143} % vi /var/tmp/lp
^[*rbC^[*t300R^[&l2aolE^[*o1d2Q....

As shown by the lpq status above, the a2ps program was invoked to convert a text file into PostScript, which was then rasterized by GhostScript.


Next Previous Contents