Every filter receives a fixed set of environment variables that can
be used by the filter:
The first task of any filter is to ensure that the correct number of
command-line arguments are present:
After the options have been processed, the filter writes PostScript code
to the standard output based on the print file, closes the print file (as
needed), and returns 0 to the scheduler.
Filters that produce PostScript output must generate output conforming
to the Adobe Document Structuring Conventions, 3.0. In general this means
the beginning of each file must begin with:
These comments allow the PostScript filter to correctly perform page
accounting, copy generation, N-up printing, and so forth.
This chapter discusses how to write a printer driver, which is a
special filter program that converts CUPS raster data into the
appropriate commands and data required for a printer.
Raster printers utilitize PPD files that specify one or more
device-specific filters that handle converting print files for the
printer. The simplest raster printer drivers provide a single filter
that converts CUPS raster data to the printer's native format.
After the page dictionary comes the page data which is a full-resolution,
uncompressed bitmap representing the page in the printer's output colorspace.
Printer drivers must handle all page accounting. This means they must
send "PAGE:" messages to the standard error file for each page (and in many
cases, copy) sent to the printer.
Besides the standard PostScript page device dictionary variables defined
in the Adobe PostScript Level 3 reference manual, the CUPS filters support
additional variables that are passed in the page device dictionary header for
the page and in some cases control the type of raster data that is generated:
Bitmaps with a colorspace of CUPS_CSPACE_KCMYcm and more than 1 bit per
color are transmitted to the raster driver in KCMY colorspace; the driver
is responsible for producing the correct separation of normal and light
cyan and magenta inks.
PPD files play an important part of all raster printer drivers. Options
defined in the PPD file contain PostScript commands that control the raster
data that is sent to the printer driver.
Each option begins with the option name followed by the computer and
human-readable values. The PostScript commands follow these inside double
quotes. PostScript commands can be provided on a single line:
The choice of the two formats is usually esthetic. However, each line in
a PPD file must not exceed 255 characters, so if your PostScript commands are
long you may need to break them up on separate lines.
As with any filter, your printer driver should handle raster data from
a filename specified on the command-line or from the standard input. The
cupsRasterOpen()
function opens
a raster stream for printing:
Once you have opened the raster stream you just need to read each
page and print it:
This chapter describes how to write a backend for CUPS. Backends
communicate directly with printers and allow printer drivers and
filters to send data using any type of connection transparently.
Backends are special filters that communicate with printers directly.
They are treated slightly differently than filters, however, and have some
unique requirements.
Backends are run as the root user, so special care must be taken to
avoid potential security violations. In particular, remember that a backend
will be able to manipulate disk files, devices, and other resources that
potentially could damage a system or printer.
Besides the standard filter arguments, backends are also run with no
arguments to get a list of available devices. This discovery process is
described later in this chapter.
Like filters, backends should send multiple copies of the print file only
if a filename is supplied on the command-line. Otherwise the backend should
assume that the upstream filter has already added the necessary commands or
data to produce the multiple copies.
Backend filters generally do not do page accounting, however they should
at a minimum produce a single page message for each copy that is produced
when a filename is present on the command-line. This is because the user
selected "raw" printing and no other accounting information is possible.
Backends that talk to local character or block devices should open the
device file in exclusive mode (O_EXCL
) to cooperate with other
printers defined for the same device.
To prevent excess CPU utilitization, the backend should go to sleep
for an amount of time between retries; the CUPS-supplied backends retry
once every 30 seconds.
The serial port backend provides support for serial printers. Since
it does everything a good backend needs to do, it provides an excellent
example of what to do.
As previously noted, backends are special filter programs that talk
to printer devices. Another task a backend must perform is to list the
available devices it supports. The backend lists the available devices
when no additioanl arguments are supplied on the command-line (i.e.
just the command name...)
Once it finds a serial port it writes a single line for each port to
the standard output file. Each line looks like this:
The last two strings are the model and description for the port. The
"Unknown" string means that the printer model is unknown - some devices
are able to provide a make and model such as "HP DeskJet" that allows
users and software to choose an appropriate printer driver more easily.
Both the model and description must be enclosed inside double quotes.
As noted previously, all backends should open device files in exclusive
mode, and retry as needed until the port is available. The serial port does
this using a do-while
loop:
If the port is busy or in use by another process, the backend will
go to sleep for 30 seconds and try again. If another error is detected
a message is sent to the user and the backend aborts the print job
until the problem can be corrected.
Network and character devices pose an interesting problem when writing
data to the port - they may not be able to write all of the bytes in your
buffer before returning. To work around this problem you must loop until
all bytes have been written:
Once you have sent the print file, return 0 if the file printed
successfully or 1 if it did not. This will allow the scheduler to stop
the print job if there is a device error, preserving the print job for
later printing once the problem has been corrected.