6.5. The Print Filter of LPRng and lpdfilter

The print filter used in conjunction with LPRng is lpdfilter, which is installed as a package with the same name. The following is a detailed description of the steps involved in processing a print job. If you need to know about the inner workings of the print filter, read the scripts powering it (in particular, /usr/lib/lpdfilter/bin/if) and probably also follow the steps described in Section 6.5.3. “Troubleshooting Hints for lpdfilter”.

  1. The print filter (/usr/lib/lpdfilter/bin/if) determines which options to use as passed to it by the print spooler and specified by the print job's control file. Options for the queue to use are also gathered from /etc/printcap and /etc/lpdfilter/queuename/conf (where queuename is the name of the actual queue).

  2. If the ascii queue has been specified, the print filter is forced to treat the file as ASCII text. If a queue other than ascii has been specified, the printer filter tries to autodetect the file type. The filter determines the file type using the script /usr/lib/lpdfilter/bin/guess to run file on each file in question. The output of file is used to determine the type according to the entries in the file /etc/lpdfilter/types.

  3. The file is converted into a printer-specific data stream according to the file type and the type of queue to use:

    • If the raw queue has been specified, print data is usually sent straight to the printer or forwarded to another queue. However, data may also undergo a simple conversion through recode, if so specified in /etc/lpdfilter/queuename/conf. To have an absolute raw filter — one that bypasses lpdfilter entirely — remove the line :if=/usr/lib/lpdfilter/bin/if:\ for the corresponding queue in /etc/printcap.

    • If the queue specified is not a raw queue:

      1. If the data is not in PostScript format, it is first converted into PostScript by running /usr/lib/lpdfilter/filter/type2ps on it (where type is the actual file type determined for the data in question). For example, ASCII text is converted into PostScript with /usr/lib/lpdfilter/filter/ascii2ps, which in turn relies on a2ps to obtain the correct character encoding defined for the queue. This ensures that country-specific special characters are printed correctly in plain text files. For details, see the manual page of a2ps.

      2. If necessary, PostScript data can be converted again if a suitable script is placed in /etc/lpdfilter/queuename/pre (where queuename is the name of the actual queue to use).

      3. PostScript data is converted into another printer language, as needed.

        • If the printer is PostScript capable, the data is sent directly to the printer (or forwarded to another queue). However, data can be further processed using the Bash functions duplex and tray, which are defined in /usr/lib/lpdfilter/global/functions, to enable duplex printing and paper tray selection through PostScript commands (which requires that the PostScript printer has this functionality).

        • If the printer is not PostScript capable, Ghostscript uses a driver suitable for the native printer language of the model to produce the printer-specific data that is finally sent to the printer (or forwarded to another queue).

          Ghostscript-relevant parameters are stored either in the cm line of /etc/printcap or in the file /etc/lpdfilter/queuename/upp (where queuename is the name of the actual queue to use).

          If so desired, the Ghostscript output can be reformatted again, if a suitable script is placed in /etc/lpdfilter/queuename/post (where queuename is the name of the actual queue to use).

      4. The printer-specific data is transferred to the printer (or to another queue). Control sequences for a specific printer can be sent to the printer both before and after the data stream. These must be specified in /etc/lpdfilter/ queuename/conf.

6.5.1. Configuration of lpdfilter

Normally, the printing system is configured with YaST (as described in Section 5.3. “Configuring a Printer with YaST”), which includes the setup of lpdfilter.

Some of the more special settings, however, can only be changed by editing the configuration files of the print filter by hand. For each queue, a dedicated configuration file is written to /etc/lpdfilter/ queuename/conf (where queuename is the name of the actual queue to be used).

6.5.2. Customization of lpdfilter

  1. By default, files not in PostScript format are converted into that format with /usr/lib/lpdfilter/filter/type2ps (where type is the actual type of the file in question).

    If a suitable script is placed in /etc/lpdfilter/queuename/type2ps, it will be used for the PostScript conversion of the file. The script must be able to accept data on stdin and to output data in PostScript format on stdout.

  2. If so desired, an additional step can be performed to reformat PostScript data, which requires a suitable script be placed in /etc/lpdfilter/ queuename/pre. This may be a script to add custom PostScript preloads, for example. The script must be able to accept data on stdin and to output data in PostScript format on stdout. Some programs to reformat PostScript are included in the psutils. In particular, the program pstops is capable of performing extensive transformations. See the manual page of pstops for details.

  3. Special Ghostscript parameters: When writing the configuration with YaST, Ghostscript parameters are stored in /etc/lpdfilter/queuename/upp (where queuename is the name of the actual queue to use), but custom Ghostscript parameters can also be added to this file manually. For details on Ghostscript parameters, read Section 6.6. “Working with Ghostscript”.

  4. If so desired, data can be reformatted again after conversion by Ghostscript. This requires a suitable script be placed in /etc/lpdfilter/queuename/post (where queuename is the name of the actual queue to use). This script must be able to accept data on stdin and to output a data stream suitable for the specific printer model on stdout.

6.5.2.1. A Hardware-Independent Example

For the purposes of this example, suppose there is a queue called testqueue, which should be configured so ASCII text is printed with line numbers along the left margin. Apart from that, all files should be printed with two pages scaled to fit on one sheet. The scripts /etc/lpdfilter/testqueue/ascii2ps and /etc/lpdfilter/testqueue/pre, as shown below, would achieve that:

Example 6.3. /etc/lpdfilter/testqueue/ascii2ps: ASCII to PostScript Conversion

#!/bin/bash
cat -n - | a2ps -1 --stdin=' ' -o -

Example 6.4. /etc/lpdfilter/test/pre: PostScript Reformatting

#!/bin/bash
pstops -q '2:0L@0.6(20cm,2cm)+1L@0.6(20cm,15cm)'

These scripts need to be made executable for all users, which can be achieved with the chmod command:

chmod -v a+rx /etc/lpdfilter/test/ascii2ps
chmod -v a+rx /etc/lpdfilter/test/pre

Reformatting files with pstops only works with PostScript files created to allow such transformations, as is usually the case.

6.5.2.2. Using Custom PostScript Preloads

PostScript preloads are small files containing PostScript commands that preceed the print data stream to initialize the printer or the Ghostscript program in the desired way. PostScript preloads are mostly used to enable duplex printing on PostScript printers or to activate a special paper tray. They can also be used for margin and gamma adjustments.

To use preloads, the PostScript printer or Ghostscript must be able to interpret the special commands. Ghostscript, for instance, does not interpret commands related to duplex printing or paper trays.

For this example, the queue testqueue is again used:

Duplex Printing

To enable or disable duplex printing, create the files /etc/lpdfilter/testqueue/duplexon.ps and /etc/lpdfilter/testqueue/duplexoff.ps with the following contents:

Example 6.5. /etc/lpdfilter/testqueue/duplexon.ps: Enabling Duplex Printing

%!PS
statusdict /setduplexmode known 
{statusdict begin true setduplexmode end} if {} pop

Example 6.6. /etc/lpdfilter/testqueue/duplexoff.ps: Disabling Duplex Printing

%!PS
statusdict /setduplexmode known
{statusdict begin false setduplexmode end} if {} pop

The following PostScript code can be used to revolve the back by 180 degrees for duplex printing:

%!PS
statusdict /setduplexmode known
{statusdict begin true setduplexmode end} if {} pop 
statusdict /settumble known
{statusdict begin true settumble end} if {} pop
Paper Tray Selection

To enable the default paper tray 0 or tray number 2, create the files /etc/lpdfilter/testqueue/tray0.ps and /etc/lpdfilter/testqueue/tray2.ps:

Example 6.7. /etc/lpdfilter/testqueue/tray0.ps: Enabling Tray 0

%!PS
statusdict /setpapertray known
{statusdict begin 0 setpapertray end} if {} pop

Example 6.8. /etc/lpdfilter/testqueue/tray2.ps: Enabling Tray 2

%!PS
statusdict /setpapertray known
{statusdict begin 2 setpapertray end} if {} pop
Margin Settings

To adjust margin settings, create a file like /etc/lpdfilter/testqueue/margin.ps.

Example 6.9. /etc/lpdfilter/testqueue/margin.ps: Margin Adjustments

%!PS 
<<
/.HWMargins [left bottom right top]
/PageSize [width height]
/Margins [left-offset top-offset] 
>>
setpagedevice

The margin settings left, bottom, right, and top and the paper size measures width and height are specified in points (with one point equaling 1/72 inches or about 0.35 mm). The margin offsets left-offset and top-offset are specified in pixels, so depend on the resolution of the output device.

To change only the position of the printed area, it is sufficient to create a file like /etc/lpdfilter/testqueue/offset.ps.

Example 6.10. /etc/lpdfilter/testqueue/offset.ps: Changing the Position of the Printed Area

%!PS
<< /Margins [left-offset top-offset]
>> setpagedevice
Gamma Correction

To adjust the gamma distribution between colors, use a file like /etc/lpdfilter/testqueue/cmyk.ps or /etc/lpdfilter/testqueue/rgb.ps:

Example 6.11. /etc/lpdfilter/testqueue/cmyk.ps: CMYK Gamma Correction

%!PS 
{cyan exp} {magenta exp} {yellow exp} {black exp}
setcolortransfer

Example 6.12. /etc/lpdfilter/testqueue/rgb.ps: RGB Gamma Correction

%!PS
{red exp} {green exp} {blue exp} currenttransfer
setcolortransfer

You need to know which color model is used by your printer (either CMYK or RGB) to make this work. The values to use for cyan, magenta, yellow, and black or for red, green, and blue should be determined through testing. Normally, these should be in the range between 0.001 and 9.999.

To get a rough idea of the effect of the above filtering actions on the output, display them on screen. To see how a sample file looks without gamma correction, enter:

gs -r60 \
/usr/share/doc/packages/ghostscript/examples/colorcir.ps

To see how it looks with gamma correction according to the above sample filters:

gs -r60 /etc/lpdfilter/test/cmyk.ps \
/usr/share/doc/packages/ghostscript/examples/colorcir.ps
gs -r60 /etc/lpdfilter/test/rgb.ps \
/usr/share/doc/packages/ghostscript/examples/colorcir.ps 

End the test by pressing Ctrl-C.

Resetting the Printer

To reset the printer to its original state each time, use a file like /etc/lpdfilter/testqueue/reset.ps:

Example 6.13. /etc/lpdfilter/testqueue/reset.ps: Printer Reset

%!PS
serverdict begin 0 exitserver

To activate one of the above PostScript preloads, create a file similar to /etc/lpdfilter/testqueue/pre:

Example 6.14. /etc/lpdfilter/testqueue/pre: Activating a PostScript Preload

#!/bin/bash
cat /etc/lpdfilter/testqueue/preload.ps -

In this file, replace preload.ps with the name of your custom preload file. In addition, make this script executable and readable for all users, which can be achieved with chmod in the following way:

chmod -v a+rx /etc/lpdfilter/test/pre 
chmod -v a+r /etc/lpdfilter/test/preload.ps 

Use the mechanism described above to insert PostScript commands not only before the print data, but also after it. For instance, with a script like /etc/lpdfilter/testqueue/pre, reset the printer to its original state after each print job is finished:

Example 6.15. /etc/lpdfilter/testqueue/pre: Inserting a PostScript Preload and a PostScript Reset

#!/bin/bash
cat /etc/lpdfilter/test/preload.ps - /etc/lpdfilter/test/reset.ps

6.5.2.3. A Sample GDI Printer Configuration

This section provides an example for the customized configuration of a gdi print queue. As explained in Section 5.2.3. “The Issue with GDI Printers”, it is often nearly impossible to make such printers run under Linux. However, special driver programs are available for some GDI models. In most cases, they are designed to run as Ghostscript add-ons with the driver reformatting the Ghostscript output into the printer's own language. Often these drivers make limited use of the printer's functionality, however, allowing only black-and-white printing, for example. If such a driver is available, Ghostscript can be used with it in the following way (also see Section 6.6. “Working with Ghostscript”):

  1. Ghostscript converts the PostScript data into a raster of pixel dots then uses one of its drivers to convert the rasterized image into a format appropriate for the GDI driver at a suitable resolution. Data is then passed to the GDI driver.

  2. The rasterized image is converted by the GDI driver into a data format suitable for the printer model.

For the steps described below, it is assumed that a GDI printer driver suitable for SUSE LINUX is already installed or can be downloaded from the Internet. It is also assumed that the driver works in the way described above. In some cases, you may need some familiarity with the way source code is handled under Unix or how to handle these installations (from .zip or .tar.gz archives or maybe from .rpm packages).

After unpacking such an archive, you will often find the latest installation instructions included in some of the files, typically in README or INSTALL, or even in a doc subdirectory. If you have downloaded a .tar.gz archive, you usually need to compile and install the driver yourself.

For the purposes of the example explained below, the following setup is assumed:

  • The driver program has been installed as /usr/local/bin/printerdriver.

  • The required Ghostscript driver is pbmraw with an output resolution of 600 dpi.

  • The printer is connected to the first parallel port — /dev/lp0.

The Ghostscript driver and the resolution may be different for your printer. Read the documentation included with the driver to find out about these.

First, create the gdi queue. To do so, log in as root and run lprsetup, as follows:

lprsetup -add gdi -lprng -device /dev/lp0 \
  -driver pbmraw -dpi 600 -size a4dj -auto -sf

Now, create the script /etc/lpdfilter/gdi/post:

#!/bin/bash 
/usr/local/bin/printerdriver <gdi_driver_parameters>

Read the documentation of the driver program to find out what options exist for it. Specify them under gdi_driver_parameters as needed. Make the script executable for all users and restart the print spooler:

chmod -v a+rx /etc/lpdfilter/gdi/post 
rclpd stop
rclpd start

From now on, users should be able to print with this command:

lpr -Pgdi <file>

6.5.3. Troubleshooting Hints for lpdfilter

Enable different debug levels for lpdfilter by uncommenting (removing the # sign in front of) the corresponding line of the main filter script /usr/lib/lpdfilter/bin/if.

Example 6.16. /usr/lib/lpdfilter/bin/if: Debug Levels

# DEBUG="off"
# DEBUG="low"
# DEBUG="medium"
# DEBUG="high"

With DEBUG="low" enabled, the program logs its stderr output to the file /tmp/lpdfilter.if-$$.XXXXXX (where $$ is the process ID and XXXXXX a unique random string).

With DEBUG="medium" enabled, the program logs, in addition to its own error output, the stderr output of the scripts in /usr/lib/lpdfilter/filter if these scripts are run by /usr/lib/lpdfilter/bin/if. The debugging output is written to /tmp/lpdfilter.name-$$.XXXXXX (where name is the name of the script run and $$.XXXXXX a string composed in the way described above).

With DEBUG="high" enabled, all error output is logged as above. Additionally, all output normally destined to the printer is redirected to a log file named /tmp/lpdfilter.out-$$.XXXXXX (where $$.XXXXXX is a string composed in the way described above).

To avoid losing control over the logging activity, you may want to remove the log files with rm -v /tmp/lpdfilter* before each new test run.