Send PostScript to non-PostScript printers

By Olivier Mehani <shtrom-kb@ssji.net>

Abstract

This document covers the use of GhostScript to write an LPR filter in order to print PostScript files with non-PostCript printer, like the recent cheap bubble-jet ones.

Introduction

I own a (quite old) Canon BJ-230 printer. It's a parallel one and works well when printing text-only documents. However, it is sometimes necessary to print things like graphs or special fonts. I need PostScript support.

Updates to this document

12/12/2004

GostScript

GhostScript knows how to print PostScript to many printers, including mine. The BJ-230 printer is quite the same as the BJ-200. The driver used by gs for this series of printers is bj200. The following command can be used to print a file.

# gs -sDEVICE=bj200 -dNOPAUSE -dBATCH -sOutputFile=/dev/lp0 FILE
GNU Ghostscript 7.05 (2002-04-22)
Copyright (C) 2002 artofcode LLC, Benicia, CA.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
(...)

The -dNOPAUSE parameters prevents gs from waiting after each page sent to the printer, while -dBATCH tells it to exit after processing the file.

This method works, but has several non ignorable flaws among which:

There should be better solutions...

LPRng

LPRng provides the usual command line tools like lpr, lpstat,... Most UNIX applications call these tools when printing is needed. It would be a Good Thing to print PostScript files directly using these commands.

LPRng uses the systemwide printer configuration file /etc/printcap. One interesting feature is the ability to specify filters. lpd usually pipes the file to print directly to the printer device.

A filter with GhosScript

A filter is a script which takes its input on stdin and outputs the filtered data to stdout. It would be nice to write a filter which take the PostScript file as its input and outputs the file in a language understandable by the printer. This is something GhostScript can do. A filter like the following can be written (I put this in /usr/libexec/filters/gsprint).

#!/bin/bash
/usr/bin/gs -q -sDEVICE=bj200 -dNOPAUSE -dBATCH -sOutputFile=- -

Notice the new parameter -q parameter. It prevents gs from outputing its banner which may confuse the printer. Both the input and output files are -, which mean GhostScript reads and write data using the standard input and output.

Configuring the system to use the filter

The default print queue

The default entry for the printer in /etc/printcap is:

lp:\
      :lp=/dev/lp0:\
      :sd=/var/spool/lpd:\
      :fq=true:\
      :sh

In short, the lp option specifies the device, the sd one sets the spool directory (which will receive the files to be printed), the fq one asks for a linefeed to be sent to the printer after the end of each job, and the final sh option prevent the headers and footers to be printed with each document.

I don't want to modify this so that I can still lpr text files directly. I need to add a new print queue which will use my filter.

The new print queue

Each print queue needs its own directory. They're usually located in /var/spool. The new printer will be called lpps, so the new directory will be (totally arbitrary) called lpdps and be owned by lp:lp with permissions 0611. The entry corresponding to this print queue can then be added to the printcap file under the default lp entry.

lpps:\
      :sd=/var/spool/lpdps:\
      :fq=true:\
      :if=/usr/libexec/filters/gsprint:\
      :lp=/dev/lp0:\
      :sh

The if one specifies the input filter.

Printing to the new queue

Printing to the lpps queue will only work correctly with PostScript files as GhostScript will complain if its input is not in the good language.

$ lpr -P lpps FILE

A bonus filter

Another useful utility is a2ps (Any To PostScript), which does exactly what its name says: convert anything to PostScript. It is particularly interesting to print text files, which it will format and print. We'll create an new print queue using this tool to print nice looking text files. Let's do it the hard way.

# mkdir /var/spool/lpda2ps
# chown lp.lp /var/spool/lpda2ps
# chmod 0611 /var/spool/lpda2ps
# cat >> /etc/printcap
lpa2ps:\
        :sd=/var/spool/lpda2ps:\
        :fq=true:\
        :if=/usr/bin/a2ps -o - | /usr/libexec/filters/gsprint:\
        :lp=/dev/lp0:\
        :sh
^D

The filter is a little more complicated, as the input of the filter is piped into a2ps which (PostScript) output is then piped into our GhostScript based filter.

A better filter ?

This solution works, but is not optimal. It is still necessary for the user to determine which print queue he should use depending on the type of the file. It should be interesting to write a better, more complex, filter which, depending on the type of its input, will process the data and output it through the correct program if needed to get on paper what the user intended to see. Let's do this now !

Different document types

We mainly work (or at least I do) with 3 types of documents: ASCII text, PDF and PostScript files.

PDF and PostScript file can be recognized by their header (%!PS-Adobe-x.x for PostScript files or %PDF-x.x for PDF, where x.x is the version of the format). Knowing this, it is then possible to analyze a stream and determine what the format is. We'll assume that if the stream is neither PostScript nor PDF, it is an ASCII text.

What must be done ?

Instead of blindly piping data into GhostScript, the script must have a look at the data. Data coming from stdin will be stored in a temporary file, created with mktemp to avoid race problems. Then the first line of the file will be read to determine the filetype. Finally, depending on the filetype, the file will be sent to the printer through gs, or directly (or maybe throught a2ps).

The final filter

#!/bin/bash
#
# gsprint.sh, a GhostScript based filter for non PostScript printers
# Copyright (c) Olivier Mehani <shtrom-skb@ssji.net>, 2004
#
# This script is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Parts of this script were inspired by Swecoin's TTP8200 Unix drivers
# (http://www.swecoinus.com).
#

# gs driver to use
GSDEVICE=bj200

# should we use a2ps for plain text files ?
USEA2PS=yes

MKTEMP=/usr/bin/mktemp
LOGGER=/usr/bin/logger
CAT=/bin/cat
HEAD=/bin/head
COLRM=/usr/bin/colrm
GS=/usr/bin/gs
A2PS=/usr/bin/a2ps

GSARGS="-q -sDEVICE=$GSDEVICE -dNOPAUSE -dBATCH -sOutputFile=-"

TEMPFILE=`$MKTEMP /tmp/gsprint.XXXXXX"
if [ $? -ne 0 ]; then
	$LOGGER -t gsprint "Error: couldn't create temporary file"
	exit 1
fi

# send stdin to the temporary file
cat > $TEMPFILE

if [ "`$HEAD -1 $TEMPFILE | $COLRM 3`" = "%!" ]; then
	# PostScript file
	$GS $GSARGS $TEMPFILE
elif [ "`$HEAD -1 $TEMPFILE | $COLRM 3`" = "%PDF" ]; then
	# PDF file
	$GS $GSARGS $TEMPFILE
else
	# none of the above, we assume it is a text file
	if [ "$USEA2PS" == "yes" ]; then
		$A2PS $TEMPFILE -o - | $GS $GSARGS -
	else
		# no filtering
		cat $TEMPFILE
	fi
fi

# cleanup
rm $TEMPFILE

A little warning

I couldn't test this script, I have no ink left in my printer... It may (hope so) or may not work as is. If you had to make modifications for the filter to work, please let me know.


Page generated: 2012-04-01T12:11:24+10:00
Source: $Id: lpps.xml 66 2010-06-18 04:16:48Z shtrom $
Stylesheet: $Id: page.xsl 68 2010-06-18 05:01:18Z shtrom $