PrintingContributed by &a.kelly;30 September
1995In order to use printers with FreeBSD, you will need to set them
up to work with the Berkeley line printer spooling system, also known
as the LPD spooling system. It is the standard printer control system
in FreeBSD. This section introduces the LPD spooling system, often
simply called LPD.If you are already familiar with LPD or another printer spooling
system, you may wish to skip to section Setting up the spooling
system.What the Spooler DoesLPD controls everything about a host's printers. It is
responsible for a number of things:It controls access to attached printers and printers
attached to other hosts on the network.It enables users to submit files to be printed; these
submissions are known as jobs.It prevents multiple users from accessing a printer at the
same time by maintaining a queue for each
printer.It can print header pages (also known
as banner or burst
pages) so users can easily find jobs they have printed in a
stack of printouts.It takes care of communications parameters for printers
connected on serial ports.It can send jobs over the network to another LPD spooler
on another host.It can run special filters to format jobs to be printed
for various printer languages or printer capabilities.It can account for printer usage.Through a configuration file, and by providing the special
filter programs, you can enable the LPD system to do all or some
subset of the above for a great variety of printer hardware.Why You Should Use the SpoolerIf you are the sole user of your system, you may be wondering
why you should bother with the spooler when you do not need access
control, header pages, or printer accounting. While it is possible
to enable direct access to a printer, you should use the spooler
anyway sinceLPD prints jobs in the background; you do not have to wait
for data to be copied to the printer.LPD can conveniently run a job to be printed through
filters to add date/time headers or convert a special file
format (such as a TeX DVI file) into a format the printer will
understand. You will not have to do these steps manually.Many free and commercial programs that provide a print
feature usually expect to talk to the spooler on your system.
By setting up the spooling system, you will more easily
support other software you may later add or already
have.Setting Up the Spooling SystemTo use printers with the LPD spooling system, you will need to
set up both your printer hardware and the LPD software. This
document describes two levels of setup:See section Simple
Printer Setup to learn how to connect a printer, tell LPD
how to communicate with it, and print plain text files to the
printer.See section Advanced Printer Setup to find out how to print a
variety of special file formats, to print header pages, to
print across a network, to control access to printers, and to
do printer accounting.Simple Printer SetupThis section tells how to configure printer hardware and the
LPD software to use the printer. It teaches the basics:Section Hardware
Setup gives some hints on connecting the printer to a port
on your computer.Section Software
Setup shows how to setup the LPD spooler configuration
file /etc/printcap.If you are setting up a printer that uses a network protocol to
accept data to print instead of a serial or parallel interface, see
Printers
With Networked Data Stream Interaces.Although this section is called “Simple Printer Setup,” it is
actually fairly complex. Getting the printer to work with your
computer and the LPD spooler is the hardest part. The advanced
options like header pages and accounting are fairly easy once you
get the printer working.Hardware SetupThis section tells about the various ways you can connect a
printer to your PC. It talks about the kinds of ports and cables,
and also the kernel configuration you may need to enable FreeBSD
to speak to the printer.If you have already connected your printer and have
successfully printed with it under another operating system, you
can probably skip to section Software Setup.Ports and CablesNearly all printers you can get for a PC today support one
or both of the following interfaces:Serial interfaces use a serial
port on your computer to send data to the printer. Serial
interfaces are common in the computer industry and cables
are readily available and also easy to construct. Serial
interfaces sometimes need special cables and might require
you to configure somewhat complex communications options.Parallel interfaces use a
parallel port on your computer to send data to the
printer. Parallel interfaces are common in the PC market.
Cables are readily available but more difficult to
construct by hand. There are usually no communications
options with parallel interfaces, making their
configuration exceedingly simple.Parallel interfaces are sometimes known as
“Centronics” interfaces, named after the connector type
on the printer.In general, serial interfaces are slower than parallel
interfaces. Parallel interfaces usually offer just one-way
communication (computer to printer) while serial gives you
two-way. Many newer parallel ports can also receive data from
the printer, but only few printers need to send data back to the
computer. And FreeBSD does not support two-way parallel
communication yet.Usually, the only time you need two-way communication with
the printer is if the printer speaks PostScript. PostScript
printers can be very verbose. In fact, PostScript jobs are
actually programs sent to the printer; they need not produce
paper at all and may return results directly to the computer.
PostScript also uses two-way communication to tell the computer
about problems, such as errors in the PostScript program or
paper jams. Your users may be appreciative of such information.
Furthermore, the best way to do effective accounting with a
PostScript printer requires two-way communication: you ask the
printer for its page count (how many pages it has printed in its
lifetime), then send the user's job, then ask again for its page
count. Subtract the two values and you know how much paper to
charge the user.So, which interface should you use?If you need two-way communication, use a serial port.
FreeBSD does not yet support two-way communication over a
parallel port.If you do not need two-way communication and can pick
parallel or serial, prefer the parallel interface. It
keeps a serial port free for other peripherals—such as a
terminal or a modem—and is faster most of the time. It
is also easier to configure.Finally, use whatever works.Parallel PortsTo hook up a printer using a parallel interface, connect
the Centronics cable between the printer and the computer. The
instructions that came with the printer, the computer, or both
should give you complete guidance.Remember which parallel port you used on the computer. The
first parallel port is /dev/lpt0 to FreeBSD; the second is
/dev/lpt1, and so on.Serial PortsTo hook up a printer using a serial interface, connect the
proper serial cable between the printer and the computer. The
instructions that came with the printer, the computer, or both
should give you complete guidance.If you are unsure what the “proper serial cable” is, you
may wish to try one of the following alternatives:A modem cable connects each pin
of the connector on one end of the cable straight through
to its corresponding pin of the connector on the other
end. This type of cable is also known as a “DTE-to-DCE”
cable.A null-modem cable connects some
pins straight through, swaps others (send data to receive
data, for example), and shorts some internally in each
connector hood. This type of cable is also known as a
“DTE-to-DTE” cable.A serial printer cable, required
for some unusual printers, is like the null modem cable,
but sends some signals to their counterparts instead of
being internally shorted.You should also set up the communications parameters for the
printer, usually through front-panel controls or DIP switches on
the printer. Choose the highest bps (bits per second, sometimes
baud rate) rate that both your computer and
the printer can support. Choose 7 or 8 data bits; none, even,
or odd parity; and 1 or 2 stop bits. Also choose a flow control
protocol: either none, or XON/XOFF (also known as
“in-band” or “software”)
flow control. Remember these settings for the software
configuration that follows.Software SetupThis section describes the software setup necessary to print
with the LPD spooling system in FreeBSD.Here is an outline of the steps involved:Configure your kernel, if necessary, for the port you
are using for the printer; section Kernel Configuration
tells you what you need to do.Set the communications mode for the parallel port, if
you are using a parallel port; section Setting the
Communication Mode for the Parallel Port gives details.Test if the operating system can send data to the
printer. Section Checking Printer Communications gives some
suggestions on how to do this.Set up LPD for the printer by modifying the file
/etc/printcap. Section The /etc/printcap
File shows you how.Kernel ConfigurationThe operating system kernel is compiled to work with a
specific set of devices. The serial or parallel interface for
your printer is a part of that set. Therefore, it might be
necessary to add support for an additional serial or parallel
port if your kernel is not already configured for one.To find out if the kernel you are currently using supports a
serial interface, type:&prompt.root; dmesg | grep sioNWhere N is the
number of the serial port, starting from zero. If you see
output similar to the following:sio2 at 0x3e8-0x3ef irq 5 on isa
sio2: type 16550Athen the kernel supports the port.To find out if the kernel supports a parallel interface,
type:&prompt.root; dmesg | grep lptNWhere N is the
number of the parallel port, starting from zero. If you see
output similar to the following
lpt0 at 0x378-0x37f on isa then the kernel supports the port.You might have to reconfigure your kernel in order for the
operating system to recognize and use the parallel or serial
port you are using for the printer.To add support for a serial port, see the section on kernel
configuration. To add support for a parallel port, see that
section and the section that
follows.Adding /dev Entries for the PortsEven though the kernel may support communication along a
serial or parallel port, you will still need a software
interface through which programs running on the system can
send and receive data. That is what entries in the
/dev directory are for.To add a /dev
entry for a port:Become root with the su
command. Enter the root password when prompted.Change to the /dev directory:&prompt.root; cd /devType:&prompt.root; ./MAKEDEV portWhere port is the device entry for the
port you want to make. Use lpt0 for the first parallel port,
lpt1 for the second, and
so on; use ttyd0 for the
first serial port, ttyd1
for the second, and so on.Type:&prompt.root; ls -l portto make sure the device entry got
created.Setting the Communication Mode for the Parallel PortWhen you are using the parallel interface, you can choose
whether FreeBSD should use interrupt-driven or polled
communication with the printer.The interrupt-driven method is
the default with the GENERIC kernel. With this method,
the operating system uses an IRQ line to determine when
the printer is ready for data.The polled method directs the
operating system to repeatedly ask the printer if it is
ready for more data. When it responds ready, the kernel
sends more data.The interrupt-driven method is somewhat faster but uses up
a precious IRQ line. You should use whichever one
works.You can set the communications mode in two ways: by
configuring the kernel or by using the lptcontrol program.To set the communications mode by
configuring the kernel:Edit your kernel configuration file. Look for or
add an lpt0 entry. If you
are setting up the second parallel port, use lpt1 instead. Use lpt2 for the third port, and so
on.If you want interrupt-driven mode, add the
irq specifier:
device lpt0 at isa? port? tty irq N vector lptintrWhere N is the IRQ number for your
computer's parallel port.If you want polled mode, do not add the
irq specifier:
device lpt0 at isa? port? tty vector lptintrSave the file. Then configure, build, and install
the kernel, then reboot. See kernel configuration
for more details.To set the communications mode
withlptcontrol:Type:&prompt.root; lptcontrol -i -u Nto set interrupt-driven mode for
lptN.Type:&prompt.root; lptcontrol -p -u Nto set polled-mode for lptN.You could put these commands in your
/etc/rc.local file to set the mode each
time your system boots. See lptcontrol8 for more information.Checking Printer CommunicationsBefore proceeding to configure the spooling system, you
should make sure the operating system can successfully send
data to your printer. It is a lot easier to debug printer
communication and the spooling system separately.To test the printer, we will send some text to it. For
printers that can immediately print characters sent to them,
the program lptest is perfect: it generates all 96 printable ASCII characters in 96 lines.For a PostScript (or other language-based) printer, we
will need a more sophisticated test. A small PostScript
program, such as the following, will suffice:
%!PS
100 100 moveto 300 300 lineto stroke
310 310 moveto /Helvetica findfont 12 scalefont setfont
(Is this thing working?) show
showpageWhen this document refers to a printer language, I am
assuming a language like PostScript, and not Hewlett
Packard's PCL. Although PCL has great functionality, you can
intermingle plain text with its escape sequences.
PostScript cannot directly print plain text, and that is the
kind of printer language for which we must make special
accommodations.Checking a Parallel PrinterThis section tells you how to check if FreeBSD can
communicate with a printer connected to a parallel
port.To test a printer on a parallel
port:Become root with su.Send data to the printer.If the printer can print plain text, then
use lptest. Type:&prompt.root; lptest > /dev/lptNWhere N is the number of the
parallel port, starting from zero.If the printer understands PostScript or
other printer language, then send a small
program to the printer. Type:&prompt.root; cat > /dev/lptNThen, line by line, type the
program carefully as you
cannot edit a line once you have pressed RETURN
or ENTER. When you have finished entering the
program, press CONTROL+D, or whatever your end
of file key is.Alternatively, you can put the program in a
file and type:&prompt.root; cat file > /dev/lptNWhere file is the name of the
file containing the program you want to send to
the printer.You should see something print. Do not worry if the
text does not look right; we will fix such things
later.Checking a Serial PrinterThis section tells you how to check if FreeBSD can
communicate with a printer on a serial port.To test a printer on a serial
port:Become root with su.Edit the file /etc/remote.
Add the following entry:
printer:dv=/dev/port:br#bps-rate:pa=parityWhere port is the device entry for the
serial port (ttyd0,
ttyd1, etc.), bps-rate is the bits-per-second
rate at which the printer communicates, and
parity is the parity required by
the printer (either even, odd, none, or zero).Here is a sample entry for a printer connected via
a serial line to the third serial port at 19200 bps
with no parity:
printer:dv=/dev/ttyd2:br#19200:pa=noneConnect to the printer with tip. Type:&prompt.root; tip printerIf this step does not work, edit
the file /etc/remote again and
try using
/dev/cuaaN instead of /dev/ttydN.Send data to the printer.If the printer can print plain text, then
use lptest. Type:~$lptestIf the printer understands PostScript or
other printer language, then send a small
program to the printer. Type the program, line
by line, very carefully as
backspacing or other editing keys may be
significant to the printer. You may also need to
type a special end-of-file key for the printer
so it knows it received the whole program. For
PostScript printers, press CONTROL+D.Alternatively, you can put the program in a
file and type:~>fileWhere file is the name of the
file containing the program. After
tip sends the file, press any required end-of-file key.You should see something print. Do not worry if the
text does not look right; we will fix that later.Enabling the Spooler: The
/etc/printcap FileAt this point, your printer should be hooked up, your
kernel configured to communicate with it (if necessary), and you
have been able to send some simple data to the printer. Now, we
are ready to configure LPD to control access to your
printer.You configure LPD by editing the file
/etc/printcap. The LPD spooling system
reads this file each time the spooler is used, so updates to the
file take immediate effect.The format of the printcap file is straightforward. Use your favorite text editor to make changes to /etc/printcap. The format is identical to other capability files like /usr/share/misc/termcap and /etc/remote. For complete information about the format, see the cgetent3.The simple spooler configuration consists of the following
steps:Pick a name (and a few convenient aliases) for the
printer, and put them in the
/etc/printcap file; see Naming the Printer.Turn off header pages (which are on by default) by
inserting the sh capability;
see Suppressing Header Pages.Make a spooling directory, and specify its location
with the sd capability; see
Making the Spooling Directory.Set the /dev entry to use for the
printer, and note it in /etc/printcap
with the lp capability; see
Identifying the Printer
Device. Also, if the printer is on a serial port, set
up the communication parameters with the
fs, fc,
xs, and xc capabilities; see Configuring Spooler
Communications Parameters.Install a plain text input filter; see Installing the Text
FilterTest the setup by printing something with the
lpr
command; see Trying
It Out and Troubleshooting.Language-based printers, such as PostScript printers,
cannot directly print plain text. The simple setup outlined
above and described in the following sections assumes that if
you are installing such a printer you will print only files
that the printer can understand.Users often expect that they can print plain text to any of
the printers installed on your system. Programs that interface
to LPD to do their printing usually make the same assumption.
If you are installing such a printer and want to be able to
print jobs in the printer language and
print plain text jobs, you are strongly urged to add an
additional step to the simple setup outlined above: install an
automatic plain-text-to-PostScript (or other printer language)
conversion program. Section Accommodating
Plain Text Jobs on PostScript Printers tells how to do
this.Naming the PrinterThe first (easy) step is to pick a name for your printer.
It really does not matter whether you choose functional or
whimsical names since you can also provide a number aliases
for the printer.At least one of the printers specified in the
/etc/printcap should have the alias
lp. This is the default
printer's name. If users do not have the PRINTER environment
variable nor specify a printer name on the command line of any
of the LPD commands, then lp
will be the default printer they get to use.Also, it is common practice to make the last alias for a
printer be a full description of the printer, including make
and model.Once you have picked a name and some common aliases, put
them in the /etc/printcap file. The name
of the printer should start in the leftmost column. Separate
each alias with a vertical bar and put a colon after the last
alias.In the following example, we start with a skeletal
/etc/printcap that defines two printers
(a Diablo 630 line printer and a Panasonic KX-P4455 PostScript
laser printer):
#
# /etc/printcap for host rose
#
rattan|line|diablo|lp|Diablo 630 Line Printer:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:In this example, the first printer is named
rattan and has as aliases
line, diablo,
lp, and Diablo 630
Line Printer. Since it has the alias lp, it is also the default printer. The
second is named bamboo, and has
as aliases ps,
PS, S,
panasonic, and Panasonic KX-P4455 PostScript
v51.4.Suppressing Header PagesThe LPD spooling system will by default print a
header page for each job. The header
page contains the user name who requested the job, the host
from which the job came, and the name of the job, in nice
large letters. Unfortunately, all this extra text gets in the
way of debugging the simple printer setup, so we will suppress
header pages.To suppress header pages, add the sh capability to the entry for the
printer in /etc/printcap. Here is the
example /etc/printcap with sh added:
#
# /etc/printcap for host rose - no header pages anywhere
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:Note how we used the correct format: the
first line starts in the leftmost column, and subsequent lines
are indented with a single TAB. Every line in an entry except
the last ends in a backslash character.Making the Spooling DirectoryThe next step in the simple spooler setup is to make a
spooling directory, a directory where
print jobs reside until they are printed, and where a number
of other spooler support files live.Because of the variable nature of spooling directories, it
is customary to put these directories under
/var/spool. It is not necessary to
backup the contents of spooling directories, either.
Recreating them is as simple as running mkdir.It is also customary to make the directory with a name
that is identical to the name of the printer, as shown below:&prompt.root; mkdir /var/spool/printer-nameHowever, if you have a lot of printers on
your network, you might want to put the spooling directories
under a single directory that you reserve just for printing
with LPD. We will do this for our two example printers
rattan and bamboo:&prompt.root; mkdir /var/spool/lpd
&prompt.root; mkdir /var/spool/lpd/rattan
&prompt.root; mkdir /var/spool/lpd/bambooIf you are concerned about the privacy of jobs that
users print, you might want to protect the spooling
directory so it is not publicly accessible. Spooling
directories should be owned and be readable, writable, and
searchable by user daemon and group daemon, and no one else.
We will do this for our example printers:&prompt.root; chown daemon.daemon /var/spool/lpd/rattan
&prompt.root; chown daemon.daemon /var/spool/lpd/bamboo
&prompt.root; chmod 770 /var/spool/lpd/rattan
&prompt.root; chmod 770 /var/spool/lpd/bambooFinally, you need to tell LPD about these directories
using the /etc/printcap file. You
specify the pathname of the spooling directory with the
sd capability:
#
# /etc/printcap for host rose - added spooling directories
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:Note that the name of the printer starts in
the first column but all other entries describing the printer
should be indented with a tab and each line escaped with a
backslash.If you do not specify a spooling directory with sd, the spooling system will use
/var/spool/lpd as a default.Identifying the Printer DeviceIn section Adding /dev Entries for the Ports, we identified
which entry in the /dev directory FreeBSD
will use to communicate with the printer. Now, we tell LPD
that information. When the spooling system has a job to
print, it will open the specified device on behalf of the
filter program (which is responsible for passing data to the
printer).List the /dev entry pathname in the
/etc/printcap file using the lp capability.In our running example, let us assume that rattan is on the first parallel port,
and bamboo is on a sixth serial
port; here are the additions to
/etc/printcap:
#
# /etc/printcap for host rose - identified what devices to use
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:\
:lp=/dev/ttyd5:If you do not specify the lp
capability for a printer in your
/etc/printcap file, LPD uses
/dev/lp as a default.
/dev/lp currently does not exist in
FreeBSD.If the printer you are installing is connected to a
parallel port, skip to the section Installing the
Text Filter. Otherwise, be sure to follow the
instructions in the next section.Configuring Spooler Communication ParametersFor printers on serial ports, LPD can set up the bps
rate, parity, and other serial communication parameters on
behalf of the filter program that sends data to the printer.
This is advantageous since:It lets you try different communication parameters
by simply editing the /etc/printcap
file; you do not have to recompile the filter program.It enables the spooling system to use the same
filter program for multiple printers which may have
different serial communication settings.The following /etc/printcap
capabilities control serial communication parameters of the
device listed in the lp
capability:br#bps-rateSets the communications speed of the device to
bps-rate, where
bps-rate can be 50, 75,
110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, or 38400 bits-per-second.fc#clear-bitsClears the flag bits
clear-bits in the
sgttyb structure after opening
the device.fs#set-bitsSets the flag bits
set-bits in the
sgttyb structure.xc#clear-bitsClears local mode bits clear-bits after opening the
device.xs#set-bitsSets local mode bits set-bits.For more information on the bits for the
fc, fs, xc,
and xs capabilities, see the
file
/usr/include/sys/ioctl_compat.h.When LPD opens the device specified by the lp capability, it reads the flag bits in
the sgttyb structure; it clears
any bits in the fc capability,
then sets bits in the fs
capability, then applies the resultant setting. It does the
same for the local mode bits as well.Let us add to our example printer on the sixth serial
port. We will set the bps rate to 38400. For the flag bits,
we will set the TANDEM, ANYP, LITOUT, FLUSHO, and PASS8 flags.
For the local mode bits, we will set the LITOUT and PASS8
flags:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:\
:lp=/dev/ttyd5:fs#0x82000c1:xs#0x820:Installing the Text FilterWe are now ready to tell LPD what text filter to use to
send jobs to the printer. A text filter,
also known as an input filter, is a
program that LPD runs when it has a job to print. When LPD
runs the text filter for a printer, it sets the filter's
standard input to the job to print, and its standard output to
the printer device specified with the lp capability. The filter is expected
to read the job from standard input, perform any necessary
translation for the printer, and write the results to standard
output, which will get printed. For more information on the
text filter, see section Filters.For our simple printer setup, the text filter can be a
small shell script that just executes
/bin/cat to send the job to the printer.
FreeBSD comes with another filter called lpf that handles backspacing and
underlining for printers that might not deal with such
character streams well. And, of course, you can use any other
filter program you want. The filter lpf is described in detail in section
lpf: a
Text Filter.First, let us make the shell script
/usr/local/libexec/if-simple be a simple
text filter. Put the following text into that file with your
favorite text editor:
#!/bin/sh
#
# if-simple - Simple text input filter for lpd
# Installed in /usr/local/libexec/if-simple
#
# Simply copies stdin to stdout. Ignores all filter arguments.
/bin/cat && exit 0
exit 2Make the file executable:&prompt.root; chmod 555 /usr/local/libexec/if-simpleAnd then tell LPD to use it by specifying it with the
if capability in
/etc/printcap. We will add it to the two
printers we have so far in the example
/etc/printcap:
#
# /etc/printcap for host rose - added text filter
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\ :lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:\
:if=/usr/local/libexec/if-simple:Trying It OutYou have reached the end of the simple LPD setup.
Unfortunately, congratulations are not quite yet in order,
since we still have to test the setup and correct any
problems. To test the setup, try printing something. To
print with the LPD system, you use the command lpr,
which submits a job for printing.You can combine lpr
with the lptest program, introduced in section Checking Printer Communications to generate some
test text.To test the simple LPD
setup:Type:&prompt.root; lptest 20 5 | lpr -Pprinter-nameWhere printer-name is a the name of a printer
(or an alias) specified in /etc/printcap.
To test the default printer, type lpr
without any argument. Again, if you are
testing a printer that expects PostScript, send a PostScript
program in that language instead of using lptest. You
can do so by putting the program in a file and typing lpr file.For a PostScript printer, you should get the results of
the program. If you are using lptest, then your results should look like the following:
!"#$%&'()*+,-./01234
"#$%&'()*+,-./012345
#$%&'()*+,-./0123456
$%&'()*+,-./01234567
%&'()*+,-./012345678To further test the printer, try downloading larger
programs (for language-based printers) or running lptest with different arguments. For example, lptest 80 60 will produce 60 lines of 80 characters each.If the printer did not work, see the next section, Troubleshooting.TroubleshootingAfter performing the simple test with lptest, you
might have gotten one of the following results instead of the
correct printout:It worked, after awhile; or, it did not
eject a full sheet.The printer printed the above, but it sat for
awhile and did nothing. In fact, you might have
needed to press a PRINT REMAINING or FORM FEED button
on the printer to get any results to appear.If this is the case, the printer was probably
waiting to see if there was any more data for your job
before it printed anything. To fix this problem, you
can have the text filter send a FORM FEED character
(or whatever is necessary) to the printer. This is
usually sufficient to have the printer immediately
print any text remaining in its internal buffer. It
is also useful to make sure each print job ends on a
full sheet, so the next job does not start somewhere
on the middle of the last page of the previous
job.The following replacement for the shell script
/usr/local/libexec/if-simple
prints a form feed after it sends the job to the
printer:
#!/bin/sh
#
# if-simple - Simple text input filter for lpd
# Installed in /usr/local/libexec/if-simple
#
# Simply copies stdin to stdout. Ignores all filter arguments.
# Writes a form feed character (\f) after printing job.
/bin/cat && printf "\f" && exit 0
exit 2It produced the “staircase
effect.”You got the following on paper:
!"#$%&'()*+,-./01234
"#$%&'()*+,-./012345
#$%&'()*+,-./0123456You have become another victim of
the staircase effect, caused by
conflicting interpretations of what characters should
indicate a new-line. UNIX-style operating systems use
a single character: ASCII code 10, the line feed (LF).
MS-DOS, OS/2, and others uses a pair of characters,
ASCII code 10 and ASCII code 13
(the carriage return or CR). Many printers use the
MS-DOS convention for representing new-lines.When you print with FreeBSD, your text used just
the line feed character. The printer, upon seeing a
line feed character, advanced the paper one line, but
maintained the same horizontal position on the page
for the next character to print. That is what the
carriage return is for: to move the location of the
next character to print to the left edge of the
paper.Here is what FreeBSD wants your printer to
do:Printer received CRPrinter prints CRPrinter received LFPrinter prints CR + LFHere are some ways to achieve this:Use the printer's configuration switches or
control panel to alter its interpretation of
these characters. Check your printer's manual
to find out how to do this.If you boot your system into other
operating systems besides FreeBSD, you may
have to reconfigure the
printer to use a an interpretation for CR and
LF characters that those other operating
systems use. You might prefer one of the
other solutions, below.Have FreeBSD's serial line driver
automatically convert LF to CR+LF. Of course,
this works with printers on serial ports
only. To enable this
feature, set the CRMOD bit in fs capability in the
/etc/printcap file for the
printer.Send an escape code to
the printer to have it temporarily treat LF
characters differently. Consult your printer's
manual for escape codes that your printer might
support. When you find the proper escape code,
modify the text filter to send the code first,
then send the print job.Here is an example text filter for printers
that understand the Hewlett-Packard PCL escape
codes. This filter makes the printer treat LF
characters as a LF and CR; then it sends the
job; then it sends a form feed to eject the last
page of the job. It should work with nearly all
Hewlett Packard printers.
#!/bin/sh
#
# hpif - Simple text input filter for lpd for HP-PCL based printers
# Installed in /usr/local/libexec/hpif
#
# Simply copies stdin to stdout. Ignores all filter arguments.
# Tells printer to treat LF as CR+LF. Writes a form feed character
# after printing job.
printf "\033&k2G" && cat && printf "\033&l0H" && exit 0
exit 2Here is an example
/etc/printcap from a host
called orchid. It has a single printer attached
to its first parallel port, a Hewlett Packard
LaserJet 3Si named teak. It is using the
above script as its text filter:
#
# /etc/printcap for host orchid
#
teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
:lp=/dev/lpt0:sh:sd=/var/spool/lpd/teak:mx#0:\
:if=/usr/local/libexec/hpif:It overprinted each line.The printer never advanced a line. All of the
lines of text were printed on top of each other on one
line.This problem is the “opposite” of the staircase
effect, described above, and is much rarer. Somewhere,
the LF characters that FreeBSD uses to end a line are
being treated as CR characters to return the print
location to the left edge of the paper, but not also
down a line.Use the printer's configuration switches or
control panel to enforce the following interpretation
of LF and CR characters:Printer receivesPrinter printsCRCRLFCR + LFThe printer lost characters.While printing, the printer did not print a few
characters in each line. The problem might have
gotten worse as the printer ran, losing more and more
characters.The problem is that the printer cannot keep up
with the speed at which the computer sends data over a
serial line. (This problem should not occur with
printers on parallel ports.) There are two ways to
overcome the problem:If the printer supports XON/XOFF flow
control, have FreeBSD use it by specifying the
TANDEM bit in the fs capability.If the printer supports carrier flow
control, specify the MDMBUF bit in the fs capability. Make sure
the cable connecting the printer to the computer
is correctly wired for carrier flow control.If the printer does not support any flow
control, use some combination of the NLDELAY,
TBDELAY, CRDELAY, VTDELAY, and BSDELAY bits in
the fs capability
to add appropriate delays to the stream of data
sent to the printer.It printed garbage.The printer printed what appeared to be random
garbage, but not the desired text.This is usually another symptom of incorrect
communications parameters with a serial printer.
Double-check the bps rate in the br capability, and the parity
bits in the fs and
fc capabilities; make
sure the printer is using the same settings as
specified in the /etc/printcap
file.Nothing happened.If nothing happened, the problem is probably
within FreeBSD and not the hardware. Add the log file
(lf) capability to the
entry for the printer you are debugging in the
/etc/printcap file. For example,
here is the entry for rattan, with
the lf capability:
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:\
:lf=/var/log/rattan.logThen, try printing again. Check
the log file (in our example,
/var/log/rattan.log) to see any
error messages that might appear. Based on the
messages you see, try to correct the problem.If you do not specify a lf capability, LPD uses
/dev/console as a default.Using PrintersThis section tells you how to use printers you have setup with
FreeBSD. Here is an overview of the user-level commands:lprPrint jobslpqCheck printer queueslprmRemove jobs from a printer's queueThere is also an administrative command, lpc,
described in the section Administrating the
LPD Spooler, used to control printers and their queues.All three of the commands lpr, lprm, and
lpq
accept an option to specify on which
printer/queue to operate, as listed in the
/etc/printcap file. This enables you to
submit, remove, and check on jobs for various printers. If you do
not use the option, then these commands use the
printer specified in the PRINTER environment variable. Finally, if
you do not have a PRINTER environment variable, these commands
default to the printer named lp.Hereafter, the terminology default printer
means the printer named in the PRINTER environment variable, or the
printer named lp when there is no
PRINTER environment variable.Printing JobsTo print files, type:&prompt.user; lpr filename...This prints each of the listed files to the
default printer. If you list no files, lpr reads
data to print from standard input. For example, this command
prints some important system files:&prompt.user; lpr /etc/host.conf /etc/hosts.equivTo select a specific printer, type:&prompt.user; lpr -P printer-namefilename...This example prints a long listing of the
current directory to the printer named rattan:&prompt.user; ls -l | lpr -P rattanBecause no files were listed for the
lpr
command, lpr read the data to print
from standard input, which was the output of the ls
-l command.The lpr command
can also accept a wide variety of options to control formatting,
apply file conversions, generate multiple copies, and so forth.
For more information, see the section Printing Options.Checking JobsWhen you print with lpr, the
data you wish to print is put together in a package called a
“print job”, which is sent to the LPD spooling
system. Each printer has a queue of jobs, and your job waits in
that queue along with other jobs from yourself and from other
users. The printer prints those jobs in a first-come, first-served
order.To display the queue for the default printer, type
lpq. For a
specific printer, use the option. For
example, the command
&prompt.user; lpq -P bamboo shows the queue for the printer named bamboo. Here is an example of the output of
the lpq command:bamboo is ready and printing
Rank Owner Job Files Total Size
active kelly 9 /etc/host.conf, /etc/hosts.equiv 88 bytes
2nd kelly 10 (standard input) 1635 bytes
3rd mary 11 ... 78519 bytesThis shows three jobs in the queue for
bamboo. The first job, submitted by
user kelly, got assigned “job number” 9. Every
job for a printer gets a unique job number. Most of the time you
can ignore the job number, but you will need it if you want to
cancel the job; see section
Removing Jobs for
details.Job number nine consists of two files; multiple files given on
the lpr command
line are treated as part of a single job. It is the currently
active job (note the word active
under the “Rank” column), which means the printer should be
currently printing that job. The second job consists of data
passed as the standard input to thelpr
command. The third job came from user mary; it is a much larger
job. The pathname of the files she's trying to print is too long
to fit, so the lpq command
just shows three dots.The very first line of the output from lpq is also
useful: it tells what the printer is currently doing (or at least
what LPD thinks the printer is doing).The lpq command
also support a option to generate a detailed
long listing. Here is an example of lpq -l:waiting for bamboo to become ready (offline ?)
kelly: 1st [job 009rose]
/etc/host.conf 73 bytes
/etc/hosts.equiv 15 bytes
kelly: 2nd [job 010rose]
(standard input) 1635 bytes
mary: 3rd [job 011rose]
/home/orchid/mary/research/venus/alpha-regio/mapping 78519 bytesRemoving JobsIf you change your mind about printing a job, you can remove
the job from the queue with the lprm
command. Often, you can even use lprm to
remove an active job, but some or all of the job might still get
printed.To remove a job from the default printer, first use
lpq to find
the job number. Then type:&prompt.user; lprm job-numberTo remove the job from a specific printer, add
the option. The following command removes job
number 10 from the queue for the printer
bamboo:&prompt.user; lprm -P bamboo 10The lprm
command has a few shortcuts:lprm -Removes all jobs (for the default printer) belonging
to you.lprm userRemoves all jobs (for the default printer) belonging
to user. The superuser can
remove other users' jobs; you can remove only your own
jobs.lprmWith no job number, user name, or
appearing on the command line,
lprm removes the currently active job on the default printer, if it belongs to you. The superuser can remove any active job.Just use the option with the above
shortcuts to operate on a specific printer instead of the default.
For example, the following command removes all jobs for the
current user in the queue for the printer named rattan:&prompt.user; lprm -P rattan -If you are working in a networked
environment, lprm
will let you remove jobs only from the host from which the jobs
were submitted, even if the same printer is available from other
hosts. The following command sequence demonstrates this:&prompt.user; lpr -P rattan myfile
&prompt.user; rlogin orchid
&prompt.user; lpq -P rattan
Rank Owner Job Files Total Size
active seeyan 12 ... 49123 bytes
2nd kelly 13 myfile 12 bytes
&prompt.user; lprm -P rattan 13
rose: Permission denied
&prompt.user; logout
&prompt.user; lprm -P rattan 13
dfA013rose dequeued
cfA013rose dequeued
Beyond Plain Text: Printing OptionsThe lpr command
supports a number of options that control formatting text,
converting graphic and other file formats, producing multiple
copies, handling of the job, and more. This section describes the
options.Formatting and Conversion OptionsThe following lpr
options control formatting of the files in the job. Use these
options if the job does not contain plain text or if you want
plain text formatted through the pr
utility.For example, the following command prints a DVI file (from
the TeX typesetting system) named
fish-report.dvi to the printer named
bamboo:&prompt.user; lpr -P bamboo -d fish-report.dviThese options apply to every file in the job,
so you cannot mix (say) DVI and ditroff files together in a job.
Instead, submit the files as separate jobs, using a different
conversion option for each job.All of these options except and
require conversion filters installed for
the destination printer. For example, the
option requires the DVI conversion filter. Section Conversion
Filters gives details.Print cifplot files.Print DVI files.Print FORTRAN text files.Print plot data.Indent the output by number columns; if you omit
number, indent by 8
columns. This option works only with certain conversion
filters.Do not put any space between the
and the number.Print literal text data, including control
characters.Print ditroff (device independent troff)
data.-pFormat plain text with pr
before printing. See pr1 for more information.Use title on the
pr
header instead of the file name. This option has effect
only when used with the
option.Print troff data.Print raster data.Here is an example: this command prints a nicely formatted
version of the ls manual
page on the default printer:&prompt.user; zcat /usr/share/man/man1/ls.1.gz | troff -t -man | lpr -tThe zcat
command uncompresses the source of thels manual
page and passes it to the troff
command, which formats that source and makes GNU troff output
and passes it to lpr,
which submits the job to the LPD spooler. Because we used the
option tolpr, the
spooler will convert the GNU troff output into a format the
default printer can understand when it prints the job.Job Handling OptionsThe following options to lpr tell
LPD to handle the job specially:-# copiesProduce a number of copies of each file in the job
instead of just one copy. An administrator may disable
this option to reduce printer wear-and-tear and
encourage photocopier usage. See section Restricting Multiple Copies.This example prints three copies of
parser.c followed by three copies
of parser.h to the default
printer:&prompt.user; lpr -#3 parser.c parser.h-mSend mail after completing the print job. With this
option, the LPD system will send mail to your account
when it finishes handling your job. In its message, it
will tell you if the job completed successfully or if
there was an error, and (often) what the error
was.-sDo not copy the files to the spooling directory, but
make symbolic links to them instead.If you are printing a large job, you probably want
to use this option. It saves space in the spooling
directory (your job might overflow the free space on the
filesystem where the spooling directory resides). It
saves time as well since LPD will not have to copy each
and every byte of your job to the spooling
directory.There is a drawback, though: since LPD will refer to
the original files directly, you cannot modify or remove
them until they have been printed.If you are printing to a remote printer, LPD will
eventually have to copy files from the local host to
the remote host, so the option
will save space only on the local spooling directory,
not the remote. It is still useful, though.-rRemove the files in the job after copying them to
the spooling directory, or after printing them with the
option. Be careful with this
option!Header Page OptionsThese options to lpr
adjust the text that normally appears on a job's header page.
If header pages are suppressed for the destination printer,
these options have no effect. See section Header Pages
for information about setting up header pages.-C textReplace the hostname on the header page with
text. The hostname is
normally the name of the host from which the job was
submitted.-J textReplace the job name on the header page with
text. The job name is
normally the name of the first file of the job, or
stdin if you are printing standard input.-hDo not print any header page.At some sites, this option may have no effect due
to the way header pages are generated. See Header
Pages for details.Administrating PrintersAs an administrator for your printers, you have had to
install, set up, and test them. Using the lpc
command, you can interact with your printers in yet more ways.
With lpc, you
canStart and stop the printersEnable and disable their queuesRearrange the order of the jobs in each queue.First, a note about terminology: if a printer is
stopped, it will not print anything in its
queue. Users can still submit jobs, which will wait in the queue
until the printer is started or the queue is
cleared.If a queue is disabled, no user (except
root) can submit jobs for the printer. An
enabled queue allows jobs to be submitted. A
printer can be started for a disabled queue,
in which case it will continue to print jobs in the queue until
the queue is empty.In general, you have to have root privileges to use the lpc
command. Ordinary users can use the lpc command
to get printer status and to restart a hung printer only.Here is a summary of the lpc
commands. Most of the commands takes a printer-name argument to tell on which
printer to operate. You can use all
for the printer-name to mean all
printers listed in /etc/printcap.abort
printer-nameCancel the current job and stop the printer. Users
can still submit jobs if the queue's enabled.clean
printer-nameRemove old files from the printer's spooling
directory. Occasionally, the files that make up a job are
not properly removed by LPD, particularly if there have
been errors during printing or a lot of administrative
activity. This command finds files that do not belong in
the spooling directory and removes them.disable
printer-nameDisable queuing of new jobs. If the printer's
started, it will continue to print any jobs remaining in
the queue. The superuser (root) can always submit jobs,
even to a disabled queue.This command is useful while you are testing a new
printer or filter installation: disable the queue and
submit jobs as root. Other users will not be able to
submit jobs until you complete your testing and re-enable
the queue with the enable
command.down
printer-namemessageTake a printer down. Equivalent to
disable followed by
stop. The message appears as the printer's
status whenever a user checks the printer's queue with
lpq
or status with lpc
status.enable
printer-nameEnable the queue for a printer. Users can submit jobs
but the printer will not print anything until it is
started.help
command-namePrint help on the command
command-name. With no
command-name, print a summary of the
commands available.restart
printer-nameStart the printer. Ordinary users can use this
command if some extraordinary circumstance hangs LPD, but
they cannot start a printer stopped with either the
stop or down commands. The restart command is equivalent to
abort followed by start.start
printer-nameStart the printer. The printer will print jobs in its
queue.stop
printer-nameStop the printer. The printer will finish the current
job and will not print anything else in its queue. Even
though the printer is stopped, users can still submit jobs
to an enabled queue.topq
printer-namejob-or-usernameRearrange the queue for printer-name by placing the jobs
with the listed job numbers
or the jobs belonging to username at the top of the queue.
For this command, you cannot use all as
the printer-name.up
printer-nameBring a printer up; the opposite of the down command. Equivalent to
start followed by enable.lpc accepts
the above commands on the command line. If you do not enter any
commands, lpc enters
an interactive mode, where you can enter commands until you type
exit, quit, or end-of-file.Advanced Printer SetupThis section describes filters for printing specially formatted
files, header pages, printing across networks, and restricting and
accounting for printer usage.FiltersAlthough LPD handles network protocols, queuing, access
control, and other aspects of printing, most of the
real work happens in the
filters. Filters are programs that
communicate with the printer and handle its device dependencies
and special requirements. In the simple printer setup, we
installed a plain text filter—an extremely simple one that
should work with most printers (section Installing the
Text Filter).However, in order to take advantage of format conversion,
printer accounting, specific printer quirks, and so on, you should
understand how filters work. It will ultimately be the filter's
responsibility to handle these aspects. And the bad news is that
most of the time you have to provide filters
yourself. The good news is that many are generally available;
when they are not, they are usually easy to write.Also, FreeBSD comes with one,
/usr/libexec/lpr/lpf, that works with many
printers that can print plain text. (It handles backspacing and
tabs in the file, and does accounting, but that is about all it
does.) There are also several filters and filter components in
the FreeBSD ports collection.Here is what you will find in this section:Section How Filters Work, tries to give an overview of a
filter's role in the printing process. You should read this
section to get an understanding of what is happening “under
the hood” when LPD uses filters. This knowledge could help
you anticipate and debug problems you might encounter as you
install more and more filters on each of your printers.LPD expects every printer to be able to print plain text
by default. This presents a problem for PostScript (or
other language-based printers) which cannot directly print
plain text. Section Accommodating Plain Text Jobs on PostScript
Printers tells you what you should do to overcome this
problem. I recommend reading this section if you have a
PostScript printer.PostScript is a popular output format for many programs.
Even some people (myself included) write PostScript code
directly. But PostScript printers are expensive. Section
Simulating PostScript on Non-PostScript Printers
tells how you can further modify a printer's text filter to
accept and print PostScript data on a
non-PostScript printer. I recommend
reading this section if you do not have a PostScript
printer.Section Conversion Filters tells about a way you can
automate the conversion of specific file formats, such as
graphic or typesetting data, into formats your printer can
understand. After reading this section, you should be able
to set up your printers such that users can type
lpr -t to print troff data, or
lpr -d to print TeX DVI data, or
lpr -v to print raster image data, and so
forth. I recommend reading this section.Section Output Filters tells all about a not often used
feature of LPD: output filters. Unless you are printing
header pages (see Header Pages), you can probably skip that section
altogether.Section lpf: a Text Filter describes lpf, a fairly complete if simple text
filter for line printers (and laser printers that act like
line printers) that comes with FreeBSD. If you need a quick
way to get printer accounting working for plain text, or if
you have a printer which emits smoke when it sees backspace
characters, you should definitely consider lpf.How Filters WorkAs mentioned before, a filter is an executable program
started by LPD to handle the device-dependent part of
communicating with the printer.When LPD wants to print a file in a job, it starts a filter
program. It sets the filter's standard input to the file to
print, its standard output to the printer, and its standard
error to the error logging file (specified in the lf capability in
/etc/printcap, or
/dev/console by default).Which filter LPD starts and the filter's arguments depend on
what is listed in the /etc/printcap file
and what arguments the user specified for the job on thelpr
command line. For example, if the user typed lpr
-t, LPD would start the troff filter, listed in the
tf capability for the destination
printer. If the user wanted to print plain text, it would start
the if filter (this is mostly
true:
see Output Filters
for details).There are three kinds of filters you can specify in
/etc/printcap:The text filter, confusingly
called the input filter in LPD
documentation, handles regular text printing. Think of it
as the default filter. LPD expects every printer to be
able to print plain text by default, and it is the text
filter's job to make sure backspaces, tabs, or other
special characters do not confuse the printer. If you are
in an environment where you have to account for printer
usage, the text filter must also account for pages
printed, usually by counting the number of lines printed
and comparing that to the number of lines per page the
printer supports. The text filter is started with the
following argument list:
filter-name-c-wwidth-llength-iindent-n login-h hostacct-file where
appears if the job's submitted with
lpr -lwidthis the value from the pw (page width) capability
specified in /etc/printcap,
default 132lengthis the value from the pl (page length) capability,
default 66indentis the amount of the indentation from
lpr -i, default 0loginis the account name of the user printing the
filehostis the host name from which the job was
submittedacct-fileis the name of the accounting file from the
af
capability.A conversion filter converts a
specific file format into one the printer can render onto
paper. For example, ditroff typesetting data cannot be
directly printed, but you can install a conversion filter
for ditroff files to convert the ditroff data into a form
the printer can digest and print. Section
Conversion Filters tells all about them.
Conversion filters also need to do accounting, if you need
printer accounting. Conversion filters are started with
the following arguments:
filter-name-xpixel-width-ypixel-height-n login-h hostacct-file where pixel-width is the value from the
px capability (default 0)
and pixel-height is the
value from the py capability
(default 0).The output filter is used only if
there is no text filter, or if header pages are enabled.
In my experience, output filters are rarely used. Section
Output
Filters describe them. There are only two arguments
to an output filter:
filter-name-wwidth-llength which are identical to the text filters
and
arguments.Filters should also exit with the
following exit status:exit 0If the filter printed the file successfully.exit 1If the filter failed to print the file but wants LPD
to try to print the file again. LPD will restart a
filter if it exits with this status.exit 2If the filter failed to print the file and does not
want LPD to try again. LPD will throw out the
file.The text filter that comes with the FreeBSD release,
/usr/libexec/lpr/lpf, takes advantage of
the page width and length arguments to determine when to send a
form feed and how to account for printer usage. It uses the
login, host, and accounting file arguments to make the
accounting entries.If you are shopping for filters, see if they are
LPD-compatible. If they are, they must support the argument
lists described above. If you plan on writing filters for
general use, then have them support the same argument lists and
exit codes.Accommodating Plain Text Jobs on PostScript PrintersIf you are the only user of your computer and PostScript
(or other language-based) printer, and you promise to never send
plain text to your printer and to never use features of various
programs that will want to send plain text to your printer, then
you do not need to worry about this section at all.But, if you would like to send both PostScript and plain
text jobs to the printer, then you are urged to augment your
printer setup. To do so, we have the text filter detect if the
arriving job is plain text or PostScript. All PostScript jobs
must start with %! (for other
printer languages, see your printer documentation). If those are
the first two characters in the job, we have PostScript, and can
pass the rest of the job directly. If those are not the first
two characters in the file, then the filter will convert the
text into PostScript and print the result.How do we do this?If you have got a serial printer, a great way to do it is to
install lprps. lprps is a PostScript printer filter which
performs two-way communication with the printer. It updates the
printer's status file with verbose information from the printer,
so users and administrators can see exactly what the state of
the printer is (such as toner low or paper jam). But
more importantly, it includes a program called psif which detects whether the incoming
job is plain text and calls textps
(another program that comes with lprps) to convert it to PostScript. It
then uses lprps to send the job to
the printer.lprps is part of the FreeBSD
ports collection (see The Ports
Collection). You
can fetch, build and install it yourself, of course. After
installing lprps, just specify the
pathname to the psif program that
is part of lprps. If you
installed lprps from the ports
collection, use the following in the serial PostScript printer's
entry in /etc/printcap:
:if=/usr/local/libexec/psif:You should also specify the rw capability; that tells LPD to open the
printer in read-write mode.If you have a parallel PostScript printer (and therefore
cannot use two-way communication with the printer, which
lprps needs), you can use the
following shell script as the text filter:
#!/bin/sh
#
# psif - Print PostScript or plain text on a PostScript printer
# Script version; NOT the version that comes with lprps
# Installed in /usr/local/libexec/psif
#
read first_line
first_two_chars=`expr "$first_line" : '\(..\)'`
if [ "$first_two_chars" = "%!" ]; then
#
# PostScript job, print it.
#
echo $first_line && cat && printf "\004" && exit 0
exit 2
else
#
# Plain text, convert it, then print it.
#
( echo $first_line; cat ) | /usr/local/bin/textps && printf "\004" && exit 0
exit 2
fiIn the above script, textps is a program we installed
separately to convert plain text to PostScript. You can use any
text-to-PostScript program you wish. The FreeBSD ports
collection (see The Ports
Collection) includes a full featured text-to-PostScript
program called a2ps that you might want to investigate.Simulating PostScript on Non-PostScript PrintersPostScript is the de facto
standard for high quality typesetting and printing. PostScript
is, however, an expensive standard.
Thankfully, Alladin Enterprises has a free PostScript work-alike
called Ghostscript that runs with
FreeBSD. Ghostscript can read most PostScript files and can
render their pages onto a variety of devices, including many
brands of non-PostScript printers. By installing Ghostscript
and using a special text filter for your printer, you can make
your non-PostScript printer act like a real PostScript
printer.Ghostscript should be in the FreeBSD ports collection, if
you would like to install it from there. You can fetch, build,
and install it quite easily yourself, as well.To simulate PostScript, we have the text filter detect if it
is printing a PostScript file. If it is not, then the filter
will pass the file directly to the printer; otherwise, it will
use Ghostscript to first convert the file into a format the
printer will understand.Here is an example: the following script is a text filter
for Hewlett Packard DeskJet 500 printers. For other printers,
substitute the argument to thegs (Ghostscript) command. (Type gs -h to get a list of devices the current installation of Ghostscript supports.)
#!/bin/sh
#
# ifhp - Print Ghostscript-simulated PostScript on a DeskJet 500
# Installed in /usr/local/libexec/hpif
#
# Treat LF as CR+LF:
#
printf "\033&k2G" || exit 2
#
# Read first two characters of the file
#
read first_line
first_two_chars=`expr "$first_line" : '\(..\)'`
if [ "$first_two_chars" = "%!" ]; then
#
# It is PostScript; use Ghostscript to scan-convert and print it
#
/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 -sOutputFile=- - \
&& exit 0
else
#
# Plain text or HP/PCL, so just print it directly; print a form
# at the end to eject the last page.
#
echo $first_line && cat && printf "\f" && exit 0
fi
exit 2Finally, you need to notify LPD of the filter
via the if capability:
:if=/usr/local/libexec/hpif:That is it. You can type lpr plain.text and lpr
whatever.ps and both should print
successfully.Conversion FiltersAfter completing the simple setup described in Simple Printer Setup, the
first thing you will probably want to do is install conversion
filters for your favorite file formats (besides plain ASCII
text).Why Install Conversion Filters?Conversion filters make printing various kinds of files
easy. As an example, suppose we do a lot of work with the TeX
typesetting system, and we have a PostScript printer. Every
time we generate a DVI file from TeX, we cannot print it
directly until we convert the DVI file into PostScript. The
command sequence goes like this:&prompt.user; dvips seaweed-analysis.dvi
&prompt.user; lpr seaweed-analysis.psBy installing a conversion filter for DVI
files, we can skip the hand conversion step each time by
having LPD do it for us. Now, each time we get a DVI file, we
are just one step away from printing it:&prompt.user; lpr -d seaweed-analysis.dviWe got LPD to do the DVI file conversion
for us by specifying the option. Section
Formatting and
Conversion Options lists the conversion options.For each of the conversion options you want a printer to
support, install a conversion filter and
specify its pathname in /etc/printcap. A
conversion filter is like the text filter for the simple
printer setup (see section Installing the Text Filter) except that instead of
printing plain text, the filter converts the file into a
format the printer can understand.Which Conversions Filters Should I Install?You should install the conversion filters you expect to
use. If you print a lot of DVI data, then a DVI conversion
filter is in order. If you have got plenty of troff to print
out, then you probably want a troff filter.The following table summarizes the filters that LPD works
with, their capability entries for the
/etc/printcap file, and how to invoke
them with the lpr command:File type/etc/printcap
capabilitylpr optioncifplotcfDVIdfplotgfditroffnfFORTRAN textrftroffrfrastervfplain textifnone, , or In our example, using lpr -d means the
printer needs a df capability in
its entry in /etc/printcap.Despite what others might contend, formats like FORTRAN
text and plot are probably obsolete. At your site, you can
give new meanings to these or any of the formatting options
just by installing custom filters. For example, suppose you
would like to directly print Printerleaf files (files from the
Interleaf desktop publishing program), but will never print
plot files. You could install a Printerleaf conversion filter
under the gf capability and then
educate your users that lpr -g mean “print
Printerleaf files.”Installing Conversion FiltersSince conversion filters are programs you install outside
of the base FreeBSD installation, they should probably go
under /usr/local. The directory
/usr/local/libexec is a popular location,
since they are specialized programs that only LPD will run;
regular users should not ever need to run them.To enable a conversion filter, specify its pathname under
the appropriate capability for the destination printer in
/etc/printcap.In our example, we will add the DVI conversion filter to
the entry for the printer named bamboo. Here is the example
/etc/printcap file again, with the new
df capability for the printer
bamboo.
#
# /etc/printcap for host rose - added df filter for bamboo
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:The DVI filter is a shell script named
/usr/local/libexec/psdf. Here is that
script:
#!bin/sh
#
# psdf - DVI to PostScript printer filter
# Installed in /usr/local/libexec/psdf
#
# Invoked by lpd when user runs lpr -d
#
exec /usr/local/bin/dvips -f | /usr/local/libexec/lprps "$@"This script runs dvips in filter mode (the argument) on standard input, which is the job to print. It then starts the PostScript printer filter lprps (see section Accommodating Plain Text Jobs on PostScript
Printers) with the arguments LPD passed to this script.
lprps will use those arguments
to account for the pages printed.More Conversion Filter ExamplesSince there is no fixed set of steps to install
conversion filters, let me instead provide more examples. Use
these as guidance to making your own filters. Use them
directly, if appropriate.This example script is a raster (well, GIF file, actually)
conversion filter for a Hewlett Packard LaserJet III-Si
printer:
#!/bin/sh
#
# hpvf - Convert GIF files into HP/PCL, then print
# Installed in /usr/local/libexec/hpvf
PATH=/usr/X11R6/bin:$PATH; export PATH giftopnm | ppmtopgm | pgmtopbm | pbmtolj -resolution 300 \
&& exit 0 \
|| exit 2It works by converting the GIF file into a
portable anymap, converting that into a portable graymap,
converting that into a portable bitmap, and converting that
into LaserJet/PCL-compatible data.Here is the /etc/printcap file with
an entry for a printer using the above filter:
#
# /etc/printcap for host orchid
#
teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
:lp=/dev/lpt0:sh:sd=/var/spool/lpd/teak:mx#0:\
:if=/usr/local/libexec/hpif:\
:vf=/usr/local/libexec/hpvf:The following script is a conversion filter for troff data
from the groff typesetting system for the PostScript printer
named bamboo:
#!/bin/sh
#
# pstf - Convert groff's troff data into PS, then print.
# Installed in /usr/local/libexec/pstf
#
exec grops | /usr/local/libexec/lprps "$@"The above script makes use of lprps again to handle the communication
with the printer. If the printer were on a parallel port, we
would use this script instead:
#!/bin/sh
#
# pstf - Convert groff's troff data into PS, then print.
# Installed in /usr/local/libexec/pstf
#
exec gropsThat is it. Here is the entry we need to
add to /etc/printcap to enable the
filter:
:tf=/usr/local/libexec/pstf:Here is an example that might make old hands at FORTRAN
blush. It is a FORTRAN-text filter for any printer that can
directly print plain text. We will install it for the printer
teak:
#!/bin/sh
#
# hprf - FORTRAN text filter for LaserJet 3si:
# Installed in /usr/local/libexec/hprf
#
printf "\033&k2G" && fpr && printf "\f" && exit 0
exit 2And we will add this line to the
/etc/printcap for the printer
teak to enable this filter:
:rf=/usr/local/libexec/hprf:Here is one final, somewhat complex example. We will add
a DVI filter to the LaserJet printer teak introduced earlier. First, the
easy part: updating /etc/printcap with
the location of the DVI filter:
:df=/usr/local/libexec/hpdf:Now, for the hard part: making the filter. For that, we
need a DVI-to-LaserJet/PCL conversion program. The FreeBSD
ports collection (see The
Ports Collection) has one: dvi2xx is the
name of the package. Installing this package gives us the
program we need, dvilj2p, which converts DVI into LaserJet IIp, LaserJet III, and LaserJet 2000 compatible codes.dvilj2p makes
the filter hpdf quite complex since
dvilj2p cannot
read from standard input. It wants to work with a filename.
What is worse, the filename has to end in
.dvi so using
/dev/fd/0 for standard input is
problematic. We can get around that problem by linking
(symbolically) a temporary file name (one that ends in
.dvi) to /dev/fd/0,
thereby forcing dvilj2p to read from standard input.The only other fly in the ointment is the fact that we
cannot use /tmp for the temporary link.
Symbolic links are owned by user and group
bin. The filter runs as user daemon. And the
/tmp directory has the sticky bit set.
The filter can create the link, but it will not be able clean
up when done and remove it since the link will belong to a
different user.Instead, the filter will make the symbolic link in the
current working directory, which is the spooling directory
(specified by the sd capability
in /etc/printcap). This is a perfect
place for filters to do their work, especially since there is
(sometimes) more free disk space in the spooling directory
than under /tmp.Here, finally, is the filter:
#!/bin/sh
#
# hpdf - Print DVI data on HP/PCL printer
# Installed in /usr/local/libexec/hpdf
PATH=/usr/local/bin:$PATH; export PATH
#
# Define a function to clean up our temporary files. These exist
# in the current directory, which will be the spooling directory
# for the printer.
#
cleanup() {
rm -f hpdf$$.dvi
}
#
# Define a function to handle fatal errors: print the given message
# and exit 2. Exiting with 2 tells LPD to do not try to reprint the
# job.
#
fatal() {
echo "$@" 1>&2
cleanup
exit 2
}
#
# If user removes the job, LPD will send SIGINT, so trap SIGINT
# (and a few other signals) to clean up after ourselves.
#
trap cleanup 1 2 15
#
# Make sure we are not colliding with any existing files.
#
cleanup
#
# Link the DVI input file to standard input (the file to print).
#
ln -s /dev/fd/0 hpdf$$.dvi || fatal "Cannot symlink /dev/fd/0"
#
# Make LF = CR+LF
#
printf "\033&k2G" || fatal "Cannot initialize printer"
#
# Convert and print. Return value from dvilj2p does not seem to be
# reliable, so we ignore it.
#
dvilj2p -M1 -q -e- dfhp$$.dvi
#
# Clean up and exit
#
cleanup
exit 0Automated Conversion: An Alternative To Conversion
FiltersAll these conversion filters accomplish a lot for your
printing environment, but at the cost forcing the user to
specify (on the lpr
command line) which one to use. If your users are not
particularly computer literate, having to specify a filter
option will become annoying. What is worse, though, is that
an incorrectly specified filter option may run a filter on the
wrong type of file and cause your printer to spew out hundreds
of sheets of paper.Rather than install conversion filters at all, you might
want to try having the text filter (since it is the default
filter) detect the type of file it has been asked to print and
then automatically run the right conversion filter. Tools
such as file can be of help
here. Of course, it will be hard to determine the differences
between some file types—and, of course,
you can still provide conversion filters just for them.The FreeBSD ports collection has a text filter that
performs automatic conversion called apsfilter. It can detect plain text, PostScript, and DVI files, run the proper conversions, and print.Output FiltersThe LPD spooling system supports one other type of filter
that we have not yet explored: an output filter. An output
filter is intended for printing plain text only, like the text
filter, but with many simplifications. If you are using an
output filter but no text filter, then:LPD starts an output filter once for the entire job
instead of once for each file in the job.LPD does not make any provision to identify the start
or the end of files within the job for the output filter.LPD does not pass the user's login or host to the
filter, so it is not intended to do accounting. In fact,
it gets only two arguments:filter-name-wwidth-llengthWhere width is
from the pw capability and
length is from the pl capability for the printer in
question.Do not be seduced by an output filter's simplicity. If you
would like each file in a job to start on a different page an
output filter will not work. Use a text
filter (also known as an input filter); see section Installing the Text
Filter. Furthermore, an output filter is actually
more complex in that it has to examine the
byte stream being sent to it for special flag characters and
must send signals to itself on behalf of LPD.However, an output filter is necessary
if you want header pages and need to send escape sequences or
other initialization strings to be able to print the header
page. (But it is also futile if you want
to charge header pages to the requesting user's account, since
LPD does not give any user or host information to the output
filter.)On a single printer, LPD allows both an output filter and
text or other filters. In such cases, LPD will start the output
filter to print the header page (see section Header
Pages) only. LPD then expects the output filter to
stop itself by sending two bytes to the
filter: ASCII 031 followed by ASCII 001. When an output filter
sees these two bytes (031, 001), it should stop by sending
SIGSTOP to itself. When LPD's done running other filters, it
will restart the output filter by sending SIGCONT to it.If there is an output filter but no
text filter and LPD is working on a plain text job, LPD uses the
output filter to do the job. As stated before, the output
filter will print each file of the job in sequence with no
intervening form feeds or other paper advancement, and this is
probably not what you want. In almost all
cases, you need a text filter.The program lpf, which we
introduced earlier as a text filter, can also run as an output
filter. If you need a quick-and-dirty output filter but do not
want to write the byte detection and signal sending code, try
lpf. You can also wrap lpf in a shell script to handle any
initialization codes the printer might require.lpf: a Text FilterThe program /usr/libexec/lpr/lpf that
comes with FreeBSD binary distribution is a text filter (input
filter) that can indent output (job submitted with lpr -i), allow literal characters to pass
(job submitted with lpr -l), adjust the
printing position for backspaces and tabs in the job, and
account for pages printed. It can also act like an output
filter.lpf is suitable for many
printing environments. And although it has no capability to
send initialization sequences to a printer, it is easy to write
a shell script to do the needed initialization and then execute
lpf.In order for lpf to do page
accounting correctly, it needs correct values filled in for the
pw and pl capabilities in the
/etc/printcap file. It uses these values
to determine how much text can fit on a page and how many pages
were in a user's job. For more information on printer
accounting, see Accounting for Printer
Usage.Header PagesIf you have lots of users, all of them
using various printers, then you probably want to consider
header pages as a necessary evil.Header pages, also known as banner or
burst pages identify to whom jobs belong
after they are printed. They are usually printed in large, bold
letters, perhaps with decorative borders, so that in a stack of
printouts they stand out from the real documents that comprise
users' jobs. They enable users to locate their jobs quickly. The
obvious drawback to a header page is that it is yet one more sheet
that has to be printed for every job, their ephemeral usefulness
lasting not more than a few minutes, ultimately finding themselves
in a recycling bin or rubbish heap. (Note that header pages go
with each job, not each file in a job, so the paper waste might
not be that bad.)The LPD system can provide header pages automatically for your
printouts if your printer can directly print
plain text. If you have a PostScript printer, you will need an
external program to generate the header page; see Header Pages
on PostScript Printers.Enabling Header PagesIn the Simple
Printer Setup, we turned off header pages by
specifying sh (meaning “suppress
header”) in the /etc/printcap file. To
enable header pages for a printer, just remove the sh capability.Sounds too easy, right?You are right. You might have to
provide an output filter to send initialization strings to the
printer. Here is an example output filter for Hewlett Packard
PCL-compatible printers:
#!/bin/sh
#
# hpof - Output filter for Hewlett Packard PCL-compatible printers
# Installed in /usr/local/libexec/hpof
printf "\033&k2G" || exit 2 exec
/usr/libexec/lpr/lpfSpecify the path to the output filter in the
of capability. See Output Filters for more information.Here is an example /etc/printcap file
for the printer teak that we
introduced earlier; we enabled header pages and added the above
output filter:
#
# /etc/printcap for host orchid
#
teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
:lp=/dev/lpt0:sd=/var/spool/lpd/teak:mx#0:\
:if=/usr/local/libexec/hpif:\
:vf=/usr/local/libexec/hpvf:\
:of=/usr/local/libexec/hpof:Now, when users print jobs to teak, they get a header page with each
job. If users want to spend time searching for their printouts,
they can suppress header pages by submitting the job with
lpr -h; see Header Page
Options for more lpr
options.LPD prints a form feed character after the header page.
If your printer uses a different character or sequence of
characters to eject a page, specify them with the ff capability in
/etc/printcap.Controlling Header PagesBy enabling header pages, LPD will produce a long
header, a full page of large letters identifying
the user, host, and job. Here is an example (kelly printed the
job named outline from host rose):
k ll ll
k l l
k l l
k k eeee l l y y
k k e e l l y y
k k eeeeee l l y y
kk k e l l y y
k k e e l l y yy
k k eeee lll lll yyy y
y
y y
yyyy
ll
t l i
t l
oooo u u ttttt l ii n nnn eeee
o o u u t l i nn n e e
o o u u t l i n n eeeeee
o o u u t l i n n e
o o u uu t t l i n n e e
oooo uuu u tt lll iii n n eeee
r rrr oooo ssss eeee
rr r o o s s e e
r o o ss eeeeee
r o o ss e
r o o s s e e
r oooo ssss eeee
Job: outline
Date: Sun Sep 17 11:04:58 1995LPD appends a form feed after this text so
the job starts on a new page (unless you have sf (suppress form feeds) in the
destination printer's entry in
/etc/printcap).If you prefer, LPD can make a short
header; specify sb
(short banner) in the /etc/printcap file.
The header page will look like this:
rose:kelly Job: outline Date: Sun Sep 17 11:07:51 1995Also by default, LPD prints the header page
first, then the job. To reverse that, specify hl (header last) in
/etc/printcap.Accounting for Header PagesUsing LPD's built-in header pages enforces a particular
paradigm when it comes to printer accounting: header pages must
be free of charge.Why?Because the output filter is the only external program that
will have control when the header page is printed that could do
accounting, and it is not provided with any user or
host information or an accounting file, so it has
no idea whom to charge for printer use. It is also not enough
to just “add one page” to the text filter or any of the
conversion filters (which do have user and host information)
since users can suppress header pages with lpr
-h. They could still be charged for header pages
they did not print. Basically, lpr -h will
be the preferred option of environmentally-minded users, but you
cannot offer any incentive to use it.It is still not enough to have each of
the filters generate their own header pages (thereby being able
to charge for them). If users wanted the option of suppressing
the header pages with lpr -h, they will still
get them and be charged for them since LPD does not pass any
knowledge of the option to any of the
filters.So, what are your options?You can:Accept LPD's paradigm and make header pages free.Install an alternative to LPD, such as LPDng or PLP.
Section Alternatives to the Standard Spooler tells
more about other spooling software you can substitute for
LPD.Write a smart output filter.
Normally, an output filter is not meant to do anything
more than initialize a printer or do some simple character
conversion. It is suited for header pages and plain text
jobs (when there is no text (input) filter). But, if there
is a text filter for the plain text jobs, then LPD will
start the output filter only for the header pages. And
the output filter can parse the header page text that LPD
generates to determine what user and host to charge for
the header page. The only other problem with this method
is that the output filter still does not know what
accounting file to use (it is not passed the name of the
file from the af
capability), but if you have a well-known accounting file,
you can hard-code that into the output filter. To
facilitate the parsing step, use the sh (short header) capability in
/etc/printcap. Then again, all that
might be too much trouble, and users will certainly
appreciate the more generous system administrator who
makes header pages free.Header Pages on PostScript PrintersAs described above, LPD can generate a plain text header
page suitable for many printers. Of course, PostScript cannot
directly print plain text, so the header page feature of LPD is
useless—or mostly so.One obvious way to get header pages is to have every
conversion filter and the text filter generate the header page.
The filters should should use the user and host arguments to
generate a suitable header page. The drawback of this method is
that users will always get a header page, even if they submit
jobs with lpr -h.Let us explore this method. The following script takes
three arguments (user login name, host name, and job name) and
makes a simple PostScript header page:
#!/bin/sh
#
# make-ps-header - make a PostScript header page on stdout
# Installed in /usr/local/libexec/make-ps-header
#
#
# These are PostScript units (72 to the inch). Modify for A4 or
# whatever size paper you are using:
#
page_width=612
page_height=792
border=72
#
# Check arguments
#
if [ $# -ne 3 ]; then
echo "Usage: `basename $0` <user> <host> <job>" 1>&2
exit 1
fi
#
# Save these, mostly for readability in the PostScript, below.
#
user=$1
host=$2
job=$3
date=`date`
#
# Send the PostScript code to stdout.
#
exec cat <<EOF
%!PS
%
% Make sure we do not interfere with user's job that will follow
%
save
%
% Make a thick, unpleasant border around the edge of the paper.
%
$border $border moveto
$page_width $border 2 mul sub 0 rlineto
0 $page_height $border 2 mul sub rlineto
currentscreen 3 -1 roll pop 100 3 1 roll setscreen
$border 2 mul $page_width sub 0 rlineto closepath
0.8 setgray 10 setlinewidth stroke 0 setgray
%
% Display user's login name, nice and large and prominent
%
/Helvetica-Bold findfont 64 scalefont setfont
$page_width ($user) stringwidth pop sub 2 div $page_height 200 sub moveto
($user) show
%
% Now show the boring particulars
%
/Helvetica findfont 14 scalefont setfont
/y 200 def
[ (Job:) (Host:) (Date:) ] {
200 y moveto show /y y 18 sub def }
forall
/Helvetica-Bold findfont 14 scalefont setfont
/y 200 def
[ ($job) ($host) ($date) ] {
270 y moveto show /y y 18 sub def
} forall
%
% That is it
%
restore
showpage
EOFNow, each of the conversion filters and the
text filter can call this script to first generate the header
page, and then print the user's job. Here is the DVI conversion
filter from earlier in this document, modified to make a header
page:
#!/bin/sh
#
# psdf - DVI to PostScript printer filter
# Installed in /usr/local/libexec/psdf
#
# Invoked by lpd when user runs lpr -d
#
orig_args="$@"
fail() {
echo "$@" 1>&2
exit 2
}
while getopts "x:y:n:h:" option; do
case $option in
x|y) ;; # Ignore
n) login=$OPTARG ;;
h) host=$OPTARG ;;
*) echo "LPD started `basename $0` wrong." 1>&2
exit 2
;;
esac
done
[ "$login" ] || fail "No login name"
[ "$host" ] || fail "No host name"
( /usr/local/libexec/make-ps-header $login $host "DVI File"
/usr/local/bin/dvips -f ) | eval /usr/local/libexec/lprps $orig_argsNotice how the filter has to parse the
argument list in order to determine the user and host name. The
parsing for the other conversion filters is identical. The text
filter takes a slightly different set of arguments, though (see
section How Filters Work).As we have mentioned before, the above scheme, though fairly
simple, disables the “suppress header page” option (the
option) to lpr. If users wanted to save a tree (or a
few pennies, if you charge for header pages), they would not be
able to do so, since every filter's going to print a header page
with every job.To allow users to shut off header pages on a per-job basis,
you will need to use the trick introduced in section
Accounting for Header Pages: write an output filter
that parses the LPD-generated header page and produces a
PostScript version. If the user submits the job with
lpr -h, then LPD will not generate a header
page, and neither will your output filter. Otherwise, your
output filter will read the text from LPD and send the
appropriate header page PostScript code to the printer.If you have a PostScript printer on a serial line, you can
make use of lprps, which comes
with an output filter, psof, which
does the above. Note that psof
does not charge for header pages.Networked PrintingFreeBSD supports networked printing: sending jobs to remote
printers. Networked printing generally refers to two different
things:Accessing a printer attached to a remote host. You
install a printer that has a conventional serial or parallel
interface on one host. Then, you set up LPD to enable
access to the printer from other hosts on the network.
Section Printers
Installed on Remote Hosts tells how to do this.Accessing a printer attached directly to a network. The
printer has a network interface in addition (or in place of)
a more conventional serial or parallel interface. Such a
printer might work as follows:It might understand the LPD protocol and can even
queue jobs from remote hosts. In this case, it acts
just like a regular host running LPD. Follow the same
procedure in section Printers Installed on Remote Hosts to
set up such a printer.It might support a data stream network connection.
In this case, you “attach” the printer to one host
on the network by making that host responsible for
spooling jobs and sending them to the printer.
Section Printers with Networked Data Stream
Interfaces gives some suggestions on installing
such printers.Printers Installed on Remote HostsThe LPD spooling system has built-in support for sending
jobs to other hosts also running LPD (or are compatible with
LPD). This feature enables you to install a printer on one host
and make it accessible from other hosts. It also works with
printers that have network interfaces that understand the LPD
protocol.To enable this kind of remote printing, first install a
printer on one host, the printer host,
using the simple printer setup described in Simple Printer Setup. Do
any advanced setup in Advanced Printer Setup that you need. Make sure to
test the printer and see if it works with the features of LPD
you have enabled. Also ensure that the local
host has authorization to use the LPD service in the
remote host (see Restricting Jobs
from Remote Printers).If you are using a printer with a network interface that is
compatible with LPD, then the printer host
in the discussion below is the printer itself, and the
printer name is the name you configured for
the printer. See the documentation that accompanied your
printer and/or printer-network interface.Then, on the other hosts you want to have access to the
printer, make an entry in their
/etc/printcap files with the following:Name the entry anything you want. For simplicity,
though, you probably want to use the same name and aliases
as on the printer host.Leave the lp capability
blank, explicitly (:lp=:).Make a spooling directory and specify its location in
the sd capability. LPD will
store jobs here before they get sent to the printer host.Place the name of the printer host in the rm capability.Place the printer name on the printer
host in the rp
capability.That is it. You do not need to list conversion
filters, page dimensions, or anything else in the
/etc/printcap file.Here is an example. The host rose has two printers,
bamboo and rattan. We will enable users on the host
orchid to print to those printers. Here is the
/etc/printcap file for orchid (back from
section
Enabling Header Pages). It already had the entry for
the printer teak; we have added
entries for the two printers on the host rose:
#
# /etc/printcap for host orchid - added (remote) printers on rose
#
#
# teak is local; it is connected directly to orchid:
#
teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
:lp=/dev/lpt0:sd=/var/spool/lpd/teak:mx#0:\
:if=/usr/local/libexec/ifhp:\
:vf=/usr/local/libexec/vfhp:\
:of=/usr/local/libexec/ofhp:
#
# rattan is connected to rose; send jobs for rattan to rose:
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:lp=:rm=rose:rp=rattan:sd=/var/spool/lpd/rattan:
#
# bamboo is connected to rose as well:
#
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:lp=:rm=rose:rp=bamboo:sd=/var/spool/lpd/bamboo:Then, we just need to make spooling
directories on orchid:&prompt.root; mkdir -p /var/spool/lpd/rattan /var/spool/lpd/bamboo
&prompt.root; chmod 770 /var/spool/lpd/rattan /var/spool/lpd/bamboo
&prompt.root; chown daemon.daemon /var/spool/lpd/rattan /var/spool/lpd/bambooNow, users on orchid can print to
rattan and bamboo. If, for example, a user on orchid
typed
&prompt.user; lpr -P bamboo -d sushi-review.dvi the LPD system on orchid would copy the job
to the spooling directory
/var/spool/lpd/bamboo and note that it was
a DVI job. As soon as the host rose has room in its bamboo spooling directory, the two LPDs
would transfer the file to rose. The file would wait in rose's
queue until it was finally printed. It would be converted from
DVI to PostScript (since bamboo is a PostScript printer) on
rose.Printers with Networked Data Stream InterfacesOften, when you buy a network interface card for a printer,
you can get two versions: one which emulates a spooler (the more
expensive version), or one which just lets you send data to it
as if you were using a serial or parallel port (the cheaper
version). This section tells how to use the cheaper version.
For the more expensive one, see the previous section Printers
Installed on Remote Hosts.The format of the /etc/printcap file
lets you specify what serial or parallel interface to use, and
(if you are using a serial interface), what baud rate, whether
to use flow control, delays for tabs, conversion of newlines,
and more. But there is no way to specify a connection to a
printer that is listening on a TCP/IP or other network
port.To send data to a networked printer, you need to develop a
communications program that can be called by the text and
conversion filters. Here is one such example: the script
netprint takes all data on
standard input and sends it to a network-attached printer. We
specify the hostname of the printer as the first argument and
the port number to which to connect as the second argument to
netprint. Note that this supports
one-way communication only (FreeBSD to printer); many network
printers support two-way communication, and you might want to
take advantage of that (to get printer status, perform
accounting, etc.).
#!/usr/bin/perl
#
# netprint - Text filter for printer attached to network
# Installed in /usr/local/libexec/netprint
#
$#ARGV eq 1 || die "Usage: $0 <printer-hostname> <port-number>";
$printer_host = $ARGV[0];
$printer_port = $ARGV[1];
require 'sys/socket.ph';
($ignore, $ignore, $protocol) = getprotobyname('tcp');
($ignore, $ignore, $ignore, $ignore, $address)
= gethostbyname($printer_host);
$sockaddr = pack('S n a4 x8', &AF_INET, $printer_port, $address);
socket(PRINTER, &PF_INET, &SOCK_STREAM, $protocol)
|| die "Can't create TCP/IP stream socket: $!";
connect(PRINTER, $sockaddr) || die "Can't contact $printer_host: $!";
while (<STDIN>) { print PRINTER; }
exit 0;We can then use this script in various
filters. Suppose we had a Diablo 750-N line printer connected
to the network. The printer accepts data to print on port
number 5100. The host name of the printer is scrivener. Here
is the text filter for the printer:
#!/bin/sh
#
# diablo-if-net - Text filter for Diablo printer `scrivener' listening
# on port 5100. Installed in /usr/local/libexec/diablo-if-net # exec
/usr/libexec/lpr/lpf "$@" | /usr/local/libexec/netprint scrivener 5100Restricting Printer UsageThis section gives information on restricting printer usage.
The LPD system lets you control who can access a printer, both
locally or remotely, whether they can print multiple copies, how
large their jobs can be, and how large the printer queues can
get.Restricting Multiple CopiesThe LPD system makes it easy for users to print multiple
copies of a file. Users can print jobs with lpr
-#5 (for example) and get five copies of each file
in the job. Whether this is a good thing is up to you.If you feel multiple copies cause unnecessary wear and tear
on your printers, you can disable the option
to lpr by
adding the sc capability to the
/etc/printcap file. When users submit jobs
with the option, they will see:lpr: multiple copies are not allowedNote that if you have set up access to a printer remotely
(see section Printers Installed on Remote Hosts), you need
the sc capability on the remote
/etc/printcap files as well, or else users
will still be able to submit multiple-copy jobs by using another
host.Here is an example. This is the
/etc/printcap file for the host rose. The
printer rattan is quite hearty, so
we will allow multiple copies, but the laser printer bamboo's a bit more delicate, so we will
disable multiple copies by adding the sc capability:
#
# /etc/printcap for host rose - restrict multiple copies on bamboo
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:sc:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:Now, we also need to add the sc capability on the host orchid's
/etc/printcap (and while we are at it, let
us disable multiple copies for the printer teak):
#
# /etc/printcap for host orchid - no multiple copies for local
# printer teak or remote printer bamboo
teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
:lp=/dev/lpt0:sd=/var/spool/lpd/teak:mx#0:sc:\
:if=/usr/local/libexec/ifhp:\
:vf=/usr/local/libexec/vfhp:\
:of=/usr/local/libexec/ofhp:
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:lp=:rm=rose:rp=rattan:sd=/var/spool/lpd/rattan:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:lp=:rm=rose:rp=bamboo:sd=/var/spool/lpd/bamboo:sc:By using the sc
capability, we prevent the use of lpr -#, but
that still does not prevent users from running lpr
multiple times, or from submitting the same file multiple times
in one job like this:&prompt.user; lpr forsale.sign forsale.sign forsale.sign forsale.sign forsale.signThere are many ways to prevent this abuse
(including ignoring it) which you are free to explore.Restricting Access To PrintersYou can control who can print to what printers by using the
UNIX group mechanism and the rg
capability in /etc/printcap. Just place
the users you want to have access to a printer in a certain
group, and then name that group in the rg capability.Users outside the group (including root) will be greeted
with
lpr: Not a member of the restricted group
if they try to print to the controlled
printer.As with the sc (suppress
multiple copies) capability, you need to specify rg on remote hosts that also have access
to your printers, if you feel it is appropriate (see section
Printers Installed on Remote Hosts).For example, we will let anyone access the printer
rattan, but only those in group
artists can use bamboo. Here is the familiar
/etc/printcap for host rose:
#
# /etc/printcap for host rose - restricted group for bamboo
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:sc:rg=artists:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:Let us leave the other example
/etc/printcap file (for the host orchid)
alone. Of course, anyone on orchid can print to
bamboo. It might be the case that we only
allow certain logins on orchid anyway, and want them to have
access to the printer. Or not.There can be only one restricted group per printer.Controlling Sizes of Jobs SubmittedIf you have many users accessing the printers, you probably
need to put an upper limit on the sizes of the files users can
submit to print. After all, there is only so much free space on
the filesystem that houses the spooling directories, and you
also need to make sure there is room for the jobs of other
users.LPD enables you to limit the maximum byte size a file in a
job can be with the mx capability.
The units are in BUFSIZ blocks, which are 1024 bytes. If you
put a zero for this capability, there will be no limit on file
size.The limit applies to files in a job,
and not the total job size.LPD will not refuse a file that is larger than the limit you
place on a printer. Instead, it will queue as much of the file
up to the limit, which will then get printed. The rest will be
discarded. Whether this is correct behavior is up for
debate.Let us add limits to our example printers
rattan and bamboo. Since those artists' PostScript
files tend to be large, we will limit them to five megabytes.
We will put no limit on the plain text line printer:
#
# /etc/printcap for host rose
#
#
# No limit on job size:
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
#
# Limit of five megabytes:
#
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:sc:rg=artists:mx#5000:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:Again, the limits apply to the local users
only. If you have set up access to your printers remotely,
remote users will not get those limits. You will need to
specify the mx capability in the
remote /etc/printcap files as well. See
section Printers
Installed on Remote Hosts for more information on
remote printing.There is another specialized way to limit job sizes from
remote printers; see section Restricting Jobs from Remote Printers.Restricting Jobs from Remote PrintersThe LPD spooling system provides several ways to restrict
print jobs submitted from remote hosts:Host restrictionsYou can control from which remote hosts a local LPD
accepts requests with the files
/etc/hosts.equiv and
/etc/hosts.lpd. LPD checks to see
if an incoming request is from a host listed in either
one of these files. If not, LPD refuses the
request.The format of these files is simple: one host name
per line. Note that the file
/etc/hosts.equiv is also used by
the
ruserok3 protocol, and affects programs like rsh and
rcp, so be careful.For example, here is the
/etc/hosts.lpd file on the host
rose:
orchid
violet
madrigal.fishbaum.deThis means rose will accept requests
from the hosts orchid,
violet, and madrigal.fishbaum.de.
If any other host tries to access rose's LPD, LPD will
refuse them.Size restrictionsYou can control how much free space there needs to
remain on the filesystem where a spooling directory
resides. Make a file called
minfree in the spooling directory
for the local printer. Insert in that file a number
representing how many disk blocks (512 bytes) of free
space there has to be for a remote job to be
accepted.This lets you insure that remote users will not fill
your filesystem. You can also use it to give a certain
priority to local users: they will be able to queue jobs
long after the free disk space has fallen below the
amount specified in the minfree
file.For example, let us add a
minfree file for the printer
bamboo. We examine
/etc/printcap to find the spooling
directory for this printer; here is
bamboo's entry:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:sc:rg=artists:mx#5000:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:mx#5000:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:The spooling directory is the given
in the sd capability. We
will make three megabytes (which is 6144 disk blocks)
the amount of free disk space that must exist on the
filesystem for LPD to accept remote jobs:&prompt.root; echo 6144 > /var/spool/lpd/bamboo/minfreeUser restrictionsYou can control which remote users can print to
local printers by specifying the rs capability in
/etc/printcap. When rs appears in the entry for a
locally-attached printer, LPD will accept jobs from
remote hosts if the user submitting
the job also has an account of the same login name on
the local host. Otherwise, LPD refuses the job.This capability is particularly useful in an
environment where there are (for example) different
departments sharing a network, and some users transcend
departmental boundaries. By giving them accounts on
your systems, they can use your printers from their own
departmental systems. If you would rather allow them to
use only your printers and not your
compute resources, you can give them “token” accounts,
with no home directory and a useless shell like
/usr/bin/false.Accounting for Printer UsageSo, you need to charge for printouts. And why not? Paper
and ink cost money. And then there are maintenance
costs—printers are loaded with moving parts and tend to break
down. You have examined your printers, usage patterns, and
maintenance fees and have come up with a per-page (or per-foot,
per-meter, or per-whatever) cost. Now, how do you actually start
accounting for printouts?Well, the bad news is the LPD spooling system does not provide
much help in this department. Accounting is highly dependent on
the kind of printer in use, the formats being printed, and
your requirements in charging for printer
usage.To implement accounting, you have to modify a printer's text
filter (to charge for plain text jobs) and the conversion filters
(to charge for other file formats), to count pages or query the
printer for pages printed. You cannot get away with using the
simple output filter, since it cannot do accounting. See section
Filters.Generally, there are two ways to do accounting:Periodic accounting is the more
common way, possibly because it is easier. Whenever someone
prints a job, the filter logs the user, host, and number of
pages to an accounting file. Every month, semester, year,
or whatever time period you prefer, you collect the
accounting files for the various printers, tally up the
pages printed by users, and charge for usage. Then you
truncate all the logging files, starting with a clean slate
for the next period.Timely accounting is less common,
probably because it is more difficult. This method has the
filters charge users for printouts as soon as they use the
printers. Like disk quotas, the accounting is immediate.
You can prevent users from printing when their account goes
in the red, and might provide a way for users to check and
adjust their “print quotas.” But this method requires some
database code to track users and their quotas.The LPD spooling system supports both methods easily: since
you have to provide the filters (well, most of the time), you also
have to provide the accounting code. But there is a bright side:
you have enormous flexibility in your accounting methods. For
example, you choose whether to use periodic or timely accounting.
You choose what information to log: user names, host names, job
types, pages printed, square footage of paper used, how long the
job took to print, and so forth. And you do so by modifying the
filters to save this information.Quick and Dirty Printer AccountingFreeBSD comes with two programs that can get you set up
with simple periodic accounting right away. They are the text
filter lpf, described in section
lpf: a Text
Filter, and pac, a
program to gather and total entries from printer accounting
files.As mentioned in the section on filters (Filters), LPD
starts the text and the conversion filters with the name of the
accounting file to use on the filter command line. The filters
can use this argument to know where to write an accounting file
entry. The name of this file comes from the af capability in
/etc/printcap, and if not specified as an
absolute path, is relative to the spooling directory.LPD starts lpf with page width
and length arguments (from the pw
and pl capabilities). lpf uses these arguments to determine how
much paper will be used. After sending the file to the printer,
it then writes an accounting entry in the accounting file. The
entries look like this:
2.00 rose:andy
3.00 rose:kelly
3.00 orchid:mary
5.00 orchid:mary
2.00 orchid:zhangYou should use a separate accounting file for
each printer, as lpf has no file
locking logic built into it, and two lpfs might corrupt each other's entries if
they were to write to the same file at the same time. A easy way
to insure a separate accounting file for each printer is to use
af=acct in
/etc/printcap. Then, each accounting file
will be in the spooling directory for a printer, in a file named
acct.When you are ready to charge users for printouts, run
the pac
program. Just change to the spooling directory for the printer
you want to collect on and type pac. You
will get a dollar-centric summary like the following: Login pages/feet runs price
orchid:kelly 5.00 1 $ 0.10
orchid:mary 31.00 3 $ 0.62
orchid:zhang 9.00 1 $ 0.18
rose:andy 2.00 1 $ 0.04
rose:kelly 177.00 104 $ 3.54
rose:mary 87.00 32 $ 1.74
rose:root 26.00 12 $ 0.52
total 337.00 154 $ 6.74These are the arguments pac
expects:Which printer to
summarize. This option works only if there is an
absolute path in the af
capability in /etc/printcap.Sort the output by cost instead of alphabetically by
user name.Ignore host name in the accounting files. With this
option, user smith on host alpha is the same user smith
on host gamma. Without, they are different
users.Compute charges with price dollars per page or per foot
instead of the price from the pc capability in
/etc/printcap, or two cents (the
default). You can specify price as a floating point
number.Reverse the sort order.Make an accounting summary file and truncate the
accounting file.name…Print accounting information for the given user
names only.In the default summary that pac
produces, you see the number of pages printed by each user from
various hosts. If, at your site, host does not matter (because
users can use any host), run pac -m, to
produce the following summary: Login pages/feet runs price
andy 2.00 1 $ 0.04
kelly 182.00 105 $ 3.64
mary 118.00 35 $ 2.36
root 26.00 12 $ 0.52
zhang 9.00 1 $ 0.18
total 337.00 154 $ 6.74To compute the dollar amount due,
pac uses
the pc capability in the
/etc/printcap file (default of 200, or 2
cents per page). Specify, in hundredths of cents, the price per
page or per foot you want to charge for printouts in this
capability. You can override this value when you run
pac with
the option. The units for the
option are in dollars, though, not
hundredths of cents. For example,
&prompt.root; pac -p1.50
makes each page cost one dollar and fifty
cents. You can really rake in the profits by using this
option.Finally, running pac -s will save the
summary information in a summary accounting file, which is named
the same as the printer's accounting file, but with
_sum appended to the name. It then truncates
the accounting file. When you run pac
again, it rereads the summary file to get starting totals, then
adds information from the regular accounting file.How Can You Count Pages Printed?In order to perform even remotely accurate accounting, you
need to be able to determine how much paper a job uses. This is
the essential problem of printer accounting.For plain text jobs, the problem's not that hard to solve:
you count how many lines are in a job and compare it to how many
lines per page your printer supports. Do not forget to take
into account backspaces in the file which overprint lines, or
long logical lines that wrap onto one or more additional
physical lines.The text filter lpf
(introduced in lpf:
a Text Filter) takes into account these things when it does
accounting. If you are writing a text filter which needs to do
accounting, you might want to examine lpf's source code.How do you handle other file formats, though?Well, for DVI-to-LaserJet or DVI-to-PostScript conversion,
you can have your filter parse the diagnostic output of dvilj or
dvips and look to see how many pages were converted. You might be able to do similar things with other file formats and conversion programs.But these methods suffer from the fact that the printer may
not actually print all those pages. For example, it could jam,
run out of toner, or explode—and the user would still get
charged.So, what can you do?There is only one sure way to do
accurate accounting. Get a printer that
can tell you how much paper it uses, and attach it via a serial
line or a network connection. Nearly all PostScript printers
support this notion. Other makes and models do as well
(networked Imagen laser printers, for example). Modify the
filters for these printers to get the page usage after they
print each job and have them log accounting information based on
that value only. There is no line counting
nor error-prone file examination required.Of course, you can always be generous and make all printouts
free.Alternatives to the Standard SpoolerIf you have been reading straight through this manual, by now
you have learned just about everything there is to know about the
LPD spooling system that comes with FreeBSD. You can probably
appreciate many of its shortcomings, which naturally leads to the
question: “What other spooling systems are out there (and work with
FreeBSD)?”Unfortunately, I have located only two
alternatives—and they are almost identical to each other! They
are:PLP, the Portable Line Printer Spooler
SystemPLP was based on software developed by Patrick Powell
and then maintained by an Internet-wide group of developers.
The main site for the software is at ftp://ftp.iona.ie/pub/plp. There is also a web page.It is quite similar to the BSD LPD spooler, but boasts a
host of features, including:Better network support, including built-in support
for networked printers, NIS-maintained printcaps, and
NFS-mounted spooling directoriesSophisticated queue management, allowing multiple
printers on a queue, transfer of jobs between queues,
and queue redirectionRemote printer control functionsPrioritization of jobsExpansive security and access optionsLPRngLPRng, which purportedly means “LPR: the Next
Generation” is a complete rewrite of PLP. Patrick Powell
and Justin Mason (the principal maintainer of PLP)
collaborated to make LPRng. The main site for LPRng is
ftp://dickory.sdsu.edu/pub/LPRng.AcknowledgmentsI would like to thank the following people who have assisted in
the development of this document:Daniel Eischen
deischen@iworks.interworks.orgFor providing a plethora of HP filter programs for
perusal.&a.jehamby;For the Ghostscript-to-HP filter.My wife, Mary Kelly
urquhart@argyre.colorado.eduFor allowing me to spend more time with FreeBSD than
with her.