FreeBSD 6 Unleashed

Filters are where most of the actual work is done in printing. When lpd sends data to a filter, it sets the filter's STDIN (standard input) to the file that is to be printed and its STDOUT to the printer device. Conversion filters are similar to text filters except they are designed to convert non-text file formats into a format the printer can understand.

Text Filters

As its name implies, the text filter is the filter lpd uses when it receives plain text to print. Because most printers will accept plain-text data without any need for special control information, the text filter can be as simple as a shell script that uses cat to simply pass the raw data to the printer, or it can be as complex as a program that changes the data into a completely different format. An example of a complex filter is one that uses GhostScript, which takes raw PostScript data and changes it into a format that a non-PostScript printer can understand (see the next section for more information about GhostScript).

What is Postscript?

PostScript is a complex programming language designed to print and format graphics and text. It is device-independent, meaning that any printer that understands PostScript can render a PostScript document with no special drivers. PostScript was invented by Adobe in 1985 and is supported by a wide range of printers today. It became a widely popular language for describing text and images in files because it was so portable and device-independent (it was even used as the graphics engine for the NeXTSTEP operating system, rendering on the screen what most people would only consider sending to printers).

Unfortunately, printers that do not understand PostScript cannot print PostScript documents. (They will print out their contents as plain text, and the output is generally unreadable.) However, a freely available program called GhostScript can translate PostScript into a format that non-PostScript printers can understand. This allows these non-PostScript printers to emulate PostScript with software. GhostScript will be covered later in this chapter.

A very simple text filter for lpd would look like this:

#!/bin/sh /bin/cat && exit 0 exit 2

The generally accepted place to store a text filter is in /usr/local/libexec. (You may want to call it /usr/local/libexec/if-text, for example.) After you have saved the file, it will also need to be made executable so that lpd can run it. The command chmod 555 /usr/local/libexec/if-text will do the trick.

The first line in this filter indicates that this is a shell program that should be run with /bin/sh (the standard shell).

Tip

If any of this is unclear to you, you might want to refer to Chapter 10, "Shell Programming," before continuing with this chapter. Setting up print filters often involves a significant amount of shell programming.

The second line simply calls the cat program, which by default reads data from STDIN and sends it to STDOUT. The double ampersand (&&) means to do both of the statements on this line, or do neither one. In other words, if the cat command is successful, the script will exit with status 0, which indicates success. If there is an error, the part of the line after && is not performed, and the third line will be performed instead, which tells the program to exit with a status of 2. (If lpd receives an exit status of 2, it means the filter failed, and lpd will not try to print the file again.)

Setting Up Text Filters for Printing Plain-Text Files on PostScript Printers

The preceding simple filter will work fine for printing plain text on most non-PostScript printers. However, PostScript printers cannot handle plain-text data. (A PostScript printer expects input data in the form of a text program in the PostScript language that specifies control commands for text and graphics output.) If you have a PostScript printer, you will need a more complex filter that can convert the plain-text data into PostScript data.

The first thing you need to do is install a program that can convert plain text to PostScript. A program that can handle this task for you is a2ps, which can be found in the print section of the FreeBSD ports tree. (Refer to Chapter 16, "Installing Additional Software," for more information on how to install ports.) Note that three versions of a2ps are available. They are named based on the paper size they are intended to work with. For example, if you'll be primarily using U.S. letter paper (8.5x11"), use a2ps-letter.

A filter for a PostScript printer first has to be able to test whether it is receiving valid PostScript data. If it is, nothing happens, and the data is passed straight through as it was with the earlier simple text filter. If the PostScript printer is not receiving PostScript data, the filter must convert the data to PostScript before it sends it to the printer.

All PostScript data streams begin with the magic character sequence %!. You can write a shell script that checks to see whether the first line of the input file begins with this sequence. If it does, you have a PostScript file, and the data can be passed straight through to the printer. If it doesn't, you have a plain-text file, and the data needs to be passed through a2ps to convert it to PostScript before it is sent to the printer.

The shell script shown in Listing 17.2 will do the trick.

Listing 17.2. Sample Postscript Filter

#!/bin/sh # Simple filter for PostScript printers read header ps_test=`expr "$header" : '\(..\)'` if [ "$header" = "%!" ] then # File is PostScript. Print pass through. Echo "$header" && cat && printf "\004" && exit 0 exit 2 else # File is plain text. Convert it first. (echo "$header"; cat) | /usr/local/bin/a2p && printf "\004" && exit 0 exit 2 fi

Save this file somewhere (/usr/local/libexec/if-ps might be a good choice). Make the file executable so that lpd can run it (chmod 555 /usr/local/libexec/if-ps).

This filter code reads the first line of the input it is sent by lpd and stores it in the variable header. The program then uses the expr command to get the first two characters from the file and stores them in the variable ps_test. The if statement that comes next checks what two characters are stored in ps_test. If they are %!, this is a PostScript file and the then statement is performed, which passes the raw data directly to the printer. It sends the first line, lists the rest of the input using the cat command, sends an escaped \004 with printf, and finally exits with a status of 0. The double ampersand (&&) connects the different statements and says, "Either do all of these commands or don't do any of them." If for whatever reason any part of this command line fails, none of it will be performed, and the program will exit with status 2 insteadwhich tells lpd that the data could not be printed and that it should not retry.

If the first two characters are not %!, this file is not a PostScript file, and the else statements are performed instead. The else statements in this case first echo the first line of the program and then list (using cat) the rest of the input and pipe it to /usr/local/bin/a2ps. The a2ps program does the work of converting the file into PostScript and printing out its converted output into the filter's data stream.

Finally, when all the data has been sent, an escaped \004 is sent with printf, and the program exits with a status of 0. As with the then statement, if any part fails, the program will exit with a status of 2 instead.

The a2ps program has many options, so if you will be printing plain-text files through a PostScript printer on a regular basis, I suggest you read the man page for a2ps. You can then modify the way this script calls a2ps to suit your needs and preference.

Printing PostScript Files on Non-PostScript Printers

If you have a non-PostScript printer, your problem is just the opposite of the one just described. You need to let plain text pass through to the printer, but you need to convert PostScript data to a format that your printer can understand.

If you never plan to print more than plain text, you do not need to worry about this. However, PostScript is the common denominator in UNIX and FreeBSD when it comes to printing anything other than plain text. Virtually all word processing, graphing, drawing, and graphics programs can write a PostScript file (or send it directly to the printer). Although some applications (such as the OpenOffice.org suite) may have built-in drivers for your particular printer, most UNIX applications will not. Instead, they will simply output PostScript. Because of this, your printing capabilities will be severely limited if you cannot handle PostScript.

Fortunately, a company called Aladdin makes a freely available program called GhostScript that can simulate a PostScript printer. It takes PostScript input and converts it to a form that your printer can understand.

Note

GhostScript supports a wide variety of printers. A list (which may not be all-inclusive) can be obtained at http://www.cs.wisc.edu/~ghost/doc/printer.htm.

GhostScript is available in the FreeBSD ports collection under the print category. Refer to Chapter 16 for information on installing ports.

For this procedure to work, you will need a script similar to the one described in the previous sectionexcept that the operations will be reversed. In this case, if the script detects that the data being sent is not PostScript data, that data will be passed straight through. If the data is PostScript, it will be sent through the GhostScript program to convert it to something the non-PostScript printer can understand. The script shown in Listing 17.3, taken from the online FreeBSD Handbook, will provide PostScript functionality on an HP DeskJet 500 (a non-PostScript printer).

Listing 17.3. A Sample GhostScript Filter for an HP DeskJet 500

#!/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. # # Note that PostScript files are actually interpreted programs, # and those programs are allowed to write to stdout, which will # mess up the printed output. So, we redirect stdout to stderr # and then make descriptor 3 go to stdout, and have GhostScript # write its output there. Exercise for the clever reader: # capture the stderr output from GhostScript and mail it back to # the user originating the print job. # exec 3>&1 1>&2 /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \ -sOutputFile=/dev/fd/3 - && exit 0 # /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 "\033&l0H" && exit 0 fi exit 2

This script looks complicated at first, but it isn't really that bad. The first thing the script does is to read the first line of the input, extract the first two characters, and check to see whether they are %!. If they are, the input data is in PostScript format, and the then statements are executed, which send the output through GhostScript. If they are not, the file is treated as plain text and is passed straight through.

Note

GhostScript is a very powerful program that has far more options than can be covered here. For more information on GhostScript, see the documentation at http://www.cs.wisc.edu/~ghost.

Note

FreeBSD also comes with a program called /usr/libexec/lpr/lpf that can act as a print filter. It has many text-reformatting capabilities, and also allows for accounting (the capability to track how many pages are printed, by what users, and so on). See the man page for pac for more details on how to use pac and lpf to keep track of users' paper and printer time usage.

Conversion Filters

As mentioned earlier, conversion filters are similar to text filters, except they are designed to convert input data in any of the various formats used in user applications into a format the printer can understand. Conversion filters allow you to print various types of files directly from the command line with lpr and avoid having to load them into a program or convert them by hand before you print them. You specify which filter to use on the command line with an option to lpr. (lpr is the command used to print files from the command line. We will cover it in detail later in this chapter.)

In the modern world, the only conversion filter options that are likely to interest you are -d, which specifies a file in DVI format (as used by the TeX typesetting system), and -g, which specifies a file that contains plotting data produced by the UNIX plot routines. Other conversion filter options specify file types and applications that are not included with FreeBSD, such as -f (FORTRAN source files), -c (cifplot files), -n (ditroff), -t (C/A/T typesetting commands for ancient versions of TRoff), and -v (raster images).

By default, none of these filters is installed. Also, the option letter and the filter it calls are not hard-coded into the system. Because of this, if you have no use for a cifplot filter, for example, it is easy to redefine the -c option to call some other filter that you might have installed or written yourself. Like text filters, these user-defined filters can be shell scripts that call standard UNIX programs or standalone executable programs that may be provided by manufacturers of third-party software.

The following is an example of a very simple conversion filter that converts data in the GNU groff typesetting system (which cannot be understood by the printer) into PostScript (which can be understood by the printer, either directly or by sending the data through GhostScript):

#!/bin/sh exec grops

If you read the man page for grops, you will see that it is a program that converts groff to PostScript. In this case, the file will simply be sent through grops before being sent to the printer for printing.

If you want to install this filter, it should be saved in the directory /usr/local/libexec. (You might decide to call this filter g2ps or something similar because it converts groff data to PostScript.)

If you are planning to reassign the lpr option -t to this new filter, you'll be able to print a groff source file directly from the command line and have it come out looking correct on the printer. This would be done as follows:

# lpr -t myfile

Here, myfile is, of course, the name of the groff source file you want to print.

You learn how to configure the print system to actually use this custom filter in the following section of this chapter, where you look at setting up the /etc/printcap file (the file that controls how the print system behaves).

Категории