Creating Filters for BSD Print Spoolers
[Note: This article resulted from a project sponsored
by the U.S. Department of Energy. The U.S. Government
retains a nonexclusive,
royalty-free license in and to the copyright covering
Output devices are an ongoing source of frustration
in most computing
environments. It seems that if anything is going to
fail, it's the
printer or plotter, and most often they fail at the
moment of greatest need.
Given the seeming fragility of printer setups, the tendency
enable only the basic capabilities. On UNIX systems,
this seems especially
so -- once the printer is set up and working, there's
a great reluctance to
make any changes for fear of breaking the successful
With a simple understanding of filters, how they work,
and how to
create them, you can do a lot to extend the capabilities
of the printers
attached to your computer systems.
Before you begin, take time to understand your printer's
capabilities and how to invoke them. The manuals that
come with the
printer or plotter describe these capabilities. Don't
lose those manuals.
What Is a Filter?
A UNIX filter is a program that reads a print stream
as its input,
called stdin, modifies the data in some way, then writes
modified data on its output, called stdout.
How Are Filters Used?
UNIX's shells allow a series of commands to be chained
output of one command being piped into the next. You
can use filters
in pipelined commands, as follows:
cat file | filter | lpr
In this case, the filter modifies the output of the
in some way, then sends the modified data on to the
which prints it. Filters can play an important role
in preparing data
Using Shell Scripts for Filters
The easiest type of filter to write is a shell script,
so long as
the filter doesn't have to do anything complicated.
don't require any special compiling, and writing them
is usually straightforward.
Listing 1 shows a shell script used as a filter. This
a special printer initialization string in the output
sent to the
printer. In this example, the initialization string
is a simple reset
command. This reflects a lesson learned through experience:
if a printer
is left in some unknown state by a previous print job,
job gets printed using the format of the previous print
a reset string as the first command to the printer puts
back into a state for normal printing.
Note that the reset string includes the odd sequence
This represents a single escape character embedded in
the file. You
can generate this in vi by typing a Control-V, then
the Escape key.
Once the reset string is sent, all remaining input is
to the output with the following command:
which just says "take my error input and data input
and write it on my output." After this, the script
goes to sleep
for 10 seconds to give the printer some time to absorb
all this data,
Writing a Filter in C
I prefer writing filters in C for added flexibility.
Listing 2 represents
a filter similar to Listing 1, with a few more initialization
The program in Listing 2 sends a reset string, followed
initialization string. This particular initialization
better advantage of the inherent capabilities of the
printer. It could
just as well have been sent in a shell script. Note
in the initialization string. This is how the escape
represented in a C character string. After sending the
string, the while() loop reads the input one character
a time and copies each character to the output.
After finding the end of the input file, this filter
sends a formfeed
(\014), then executes a command to flush its output
exiting. Some printers simply hold the last page in
their buffer until
you take them offline and press the formfeed button.
Sending a formfeed
at the end of the print job is much more polite when
you are sharing
the printer with other people, as is often the case
Filters can also do more complex tasks. Listing 3 shows
a filter written
to process HP/GL files for the particular plotter we
use. This filter
sets the line style to the thinnest width. To do this,
reads the file looking for the HP/GL initialization
While searching for this command, the filter also sends
the data read
so far to the output. When it finds the IN command,
adds a PW0 command to the output data, to select a zero
pen. Once a zero-width pen is selected, the filter simply
rest of the input to the output.
This is just one example of what a filter written in
C can do. Another
filter I wrote takes a PostScript file and adds a command
the printer to use the paper in the 11x17 paper tray.
that Sun's NewSPrint product had a filter that detected
of print job submitted, then resubmitted the job to
appropriate to that job's type. Such a filter is much
for this article, but it suggests the kind of power
one can put in
Pulling Everything Together with printcap
Print filters become more convenient when they are part
of the printcap
printer definitions. printcap's definitions tell the
system how to access the printer and, in some ways,
how to format
the job for the printer (for more information do a man
A sample printcap entry looks like this:
The if= entry names the input filter file. This entry
the spooling system that every job that hasn't specified
must filter through this program. In this example, any
print job that
hasn't selected another filter runs through the sethp
shown in Listing 2.
This particular printcap entry doesn't show alternate
but one could specify several alternates. Certain letters
set aside for lpr to invoke other filters. These letters
d, g, n, r, t, v) each have
a meaning assigned to them, but for the purposes of
I will ignore these meanings. A filter invoked by the
was originally intended to handle cifplot-formatted
files, but if
you never print that type of file, you can use this
letter to invoke
your special print filter. You might use -c to invoke
mode, or -d to set a special font and line spacing.
to let the users know which filter options do what.
To add a c-filter and a d-filter to the printcap entry,
Notice that each line of a printcap entry must
end with a backslash ("\") except for the
last line, and each
line must be indented except for the first line. Also,
you must separate
each option in the printcap entry by a colon (":"),
the last line. It doesn't hurt to have extra colons,
so each line
but the first usually has a colon at the beginning and
To print a job in landscape mode they invoke the landscape
with the following command:
lpr -c filename
Instead of using filter options, you can make multiple
copies of the
printcap entry, renaming each one and change the if
filter for each to perform a different task. For instance,
create a printcap entry for a printer called landscape
and assign it setland as its input filter, users invoke
with the following command:
lpr -Plandscape filename
Multiple printcap entries create multiple logical printers.
Users just name the printer they want to send the job
-P option names the printer.
Caveats for Remote Printers
printcap entries that look like this
specify a remote printer. printcap filter options
are not passed on to remote printers. If the printcap
specifies a remote machine and printer name, any filters
in the printcap entry will not be invoked on the local
or the remote machine.
You can still invoke a filter on your print job, however.
the job before sending it to the remote printer. Pipelined
like those at the beginning of this article show how
to do this. Taking
it a step further, you can set up a C-shell alias to
handle this in
one step as for example:
alias wpr 'cat!* | filter | lpr'
Users send their print jobs and invoke the right filter
by simply typing:
Creating print filters make it easier to format print
jobs that take
advantage of a printer's or plotter's capabilities.
filter in the printcap definition simplifies user access
the command line by allowing for either an option or
a logical printer
name. Invoking special functions, such as landscape
only that the user remember how to invoke the filter.
Aliasing a pipelined
command simplifies uglier, hard-to-type UNIX shell syntax.
About the Author
Donald Brown graduated in 1981 with a BS degree in
Graphics Technology from BYU. He worked for several
years in Product
Production Engineering at Hughes Aircraft Company in
El Segundo, CA,
where his responsibilities were to take the lead in
into the department. He is currently working for Boeing
Richland, as a Software Engineer. He has written several
in C on DOS and UNIX. His current duties include UNIX
mainly on SunOS and Solaris systems.