#!/bin/sh #****************************************************************************** # File: @(#)$Id: cups-pcl3,v 2.5 2001/05/18 14:58:24 Martin Rel $ # Contents: CUPS filter converting application/vnd.cups-postscript to PCL-3 # using ghostscript with the pcl3 driver # Call: cups-pcl3 # Author: Martin Lottermoser, Greifswaldstrasse 28, 38124 Braunschweig, # Germany; Martin.Lottermoser@t-online.de # #****************************************************************************** # * # Copyright (C) 2001 by Martin Lottermoser * # All rights reserved * # * #****************************************************************************** # # Required entries in PPD files # ----------------------------- # The PPD files distributed with pcl3 are by default already configured for # this filter. If you are using one of these files, continue reading with the # next section. # # If you want to use another PPD file or ghostscript driver, add or modify the # following two statements in the PPD file: # # *cupsFilter: "application/vnd.cups-postscript 0 cups-pcl3" # *GhostscriptOptions: "" # # Replace with a sequence of command line options for ghostscript # which select the device, in the case of pcl3 also the subdevice if the device # is "pcl3". From the point of view of the PPD specification, the # *GhostscriptOptions statement may contain only a single-line QuotedValue # without hexadecimal substrings. From the point of view of this filter, the # value should not contain any characters with special meaning for the shell, # except internal field separators. # # # Installation of this filter # --------------------------- # Copy this file to the .../cups/filter directory. # # If you've not yet installed the necessary PPD files, do it now. After that # you should be able to create a queue and start to print. The remaining # comments refer to steps you may perform later if you wish. # # Exception: If the ghostscript binary you wish to use is not the one found # by calling "gs" from the filter, you'll have to set "GhostscriptCommand" in # a configuration file. Read the instructions in the next section. # # Should you encounter problems when trying to print, check CUPS' error log # file. If necessary, increase the CUPS LogLevel to "debug", restart CUPS and # try again. # # # Configuration # ------------- # This filter uses a queue-specific PostScript configuration file if it exists. # This file must have the name $PRINTER.ps ($PRINTER is the name of the queue) # and must reside in the "config" subdirectory (create it if it doesn't exist) # of the $CUPS_SERVERROOT directory, usually /etc/cups. This file will be # handed to ghostscript before the PostScript file to be printed. # # Use this configuration file for correcting misalignments, modifying # "InputAttributes" or "OutputAttributes", setting default transfer functions # or any other queue-specific configuration independent of the document to be # printed. Primarily this file should contain PostScript commands to simulate # persistent state for the printer (what you would store in a real PostScript # printer's memory in such a manner that it survives power-off or at least # end-of-job). Note that because the file to be printed will be processed after # the configuration file, the former can still override the latter. # # In addition, you can insert configuration statements for this filter into the # file. These statements must be given as PostScript comments starting in # column 1. The possiblities are: # # % DivertToFile: # CUPS' document manager does not always operate correctly. If you # suspect that the settings you have demanded do not take effect or # ghostscript gives an error when printing through this filter while the # original file is valid PostScript, set to the name of a file # which is writable for the user running this filter (usually lp). # Instead of printing, the filter will then copy its input to that file, # prepending a comment containing the command line it would use for # calling ghostscript. # # % DoAccounting: # This parameter determines whether pcl3 will issue CUPS page accounting # messages or not. The default is "True". You should set this to "False" # only if the page accounting messages are generated by the backend # (later in the print pipeline). # # % GhostscriptCommand: # Use this to set the command to invoke ghostscript. The default # corresponds to "% GhostscriptCommand: gs". # # % UseIntermediateFile: # This statement determines whether the ghostscript output file is first # written to disk or is copied directly to the printer. The former is # safer because it is legitimate for a PostScript program to invoke # operators like "print" which generate output which ghostscript maps to # standard output even for "-sOutputFile=-" (in my opinion this is a bug # in ghostscript). If you print such a document, the result is unlikely # to be usable. The same applies if you print an illegal PostScript file # because ghostscript issues some error messages on standard output. # The default is therefore "% UseIntermediateFile: True". This might # however require substantial disk space. # # Of course the file need not contain any PostScript commands if you just need # it for filter configuration. # #****************************************************************************** #name=`basename "$0"` name=cups-pcl3 # Invalid CUPS documentation printf 'DEBUG: %s: called for queue %s\n' "$name" "${PRINTER:-}" >&2 printf 'DEBUG: %s: command line: %s %s\n' "$name" "$0" "$*" >&2 # Arguments if [ $# -ne 5 -a $# -ne 6 ]; then printf 'ERROR: Usage: %s job user title copies options [filename]\n' "$0" >&2 exit 1 fi job="$1" user="$2" title="$3" copies="$4" # can be ignored in this filter (pstops generates "NumCopies") options="$5" test $# -lt 6 || exec < "$6" || exit 1 # We ignore all options completely because, as far as they are meaningful and # as far as I understand the matter, preceding filters (in particular pstops) # should have taken care of them, i.e., we assume that they have been converted # into PostScript code as far as possible. # Besides, I'm not aware of any statement in CUPS which tells a filter's # author which options can be expected (from the source code it's easiest to # find out which one needs not to expect, but that is not very interesting). #****************************************************************************** # Find configuration file for the queue cfgfile="${CUPS_SERVERROOT:-/etc/cups}/config/${PRINTER:-}.ps" if [ -f "$cfgfile" ]; then printf 'DEBUG: %s: Using configuration file %s.\n' "$name" "$cfgfile" >&2 else printf 'DEBUG: %s: No configuration file %s.\n' "$name" "$cfgfile" >&2 cfgfile= fi # Subroutine to extract filter options from the configuration file extract() { # Argument $1: name of the option sed -e "/^% $1:/!d" -e 's/^[^:]*: *//' "$cfgfile" | head -1 } # Extract filter options dacc= divert= gs_command= use_intermediate_file= if [ '' != "$cfgfile" ]; then if [ ! -r "$cfgfile" ]; then printf 'ERROR: %s: %s is not readable.\n' "$name" "$cfgfile" >&2 exit 1 fi dacc=`extract DoAccounting` divert=`extract DivertToFile` gs_command=`extract GhostscriptCommand` use_intermediate_file=`extract UseIntermediateFile` fi test '' != "$dacc" || dacc=True test '' != "$gs_command" || gs_command=gs test '' != "$use_intermediate_file" || use_intermediate_file=True printf 'DEBUG: %s: GhostscriptCommand: %s, DoAccounting: %s, '\ 'UseIntermediateFile: %s.\n' \ "$name" "$gs_command" "$dacc" "$use_intermediate_file" >&2 accounting=-dCUPSAccounting test False != "$dacc" || accounting= # Find the command line options from the PPD file. We need not bother about # *Include statements because CUPS doesn't support them. if [ ! -f "${PPD:-}" ]; then printf 'ERROR: %s: No PPD file %s.\n' "$name" "${PPD:-}" >&2 exit 1 fi printf 'DEBUG: %s: Using PPD file %s.\n' "$name" "$PPD" >&2 gs_options=`grep '^\*GhostscriptOptions:' "$PPD" | \ sed -e '1!d' -e 's/^[^"]*"//' -e 's/".*$//'` if [ '' = "$gs_options" ]; then printf 'ERROR: %s: No *GhostscriptOptions statement in %s.\n' \ "$name" "$PPD" >&2 exit 1 fi printf 'DEBUG: %s: *GhostscriptOptions: "%s"\n' "$name" "$gs_options" >&2 # Treat RIP_MAX_CACHE if [ '' != "${RIP_MAX_CACHE:-}" ]; then printf 'DEBUG: %s: RIP_MAX_CACHE is %s.\n' "$name" "$RIP_MAX_CACHE" >&2 # Convert to "MaxBitmap" value (cnf. pstoraster/gdevprn.c in CUPS) amount=`printf '%s\n' "$RIP_MAX_CACHE" | sed -e 's/[^0-9].*$//'` unit=`printf '%s\n' "$RIP_MAX_CACHE" | sed -e 's/^[0-9]*//'` if [ '' = "$amount" ]; then amount=33554432 # 32 binary mega bytes else case "$unit" in g*) amount=`expr $amount '*' 1073741824`;; # binary giga bytes # Note that this will fail on 32-bit systems if "amount" is larger than # one. m*) amount=`expr $amount '*' 1048576`;; # binary mega bytes k*) amount=`expr $amount '*' 1024`;; # binary kilo bytes *) # This applies to "t" and to no unit specification. amount=`expr $amount '*' 262144`;; # 256 K esac fi printf 'DEBUG: %s: MaxBitmap will become %s.\n' "$name" "$amount" >&2 gs_options="$gs_options -dMaxBitmap=$amount" fi #****************************************************************************** if [ '' != "$divert" ]; then cat << --- > "$divert" || { \ printf 'ERROR: %s: Failure to write to %s.\n' "$name" "$divert" >&2; \ exit 1; } %! % $name input for job $job % Call to ghostscript: $gs_command $gs_options -dCUPSMessages $accounting $cfgfile --- cat >> "$divert" || exit 1 printf 'WARNING: %s: No printing, output for job %s diverted to %s.\n' \ "$name" "$job" "$divert" >&2 exit 0 fi #****************************************************************************** if [ False = "$use_intermediate_file" ]; then # Directly to stdout, hoping that other data will not appear. However, # in order not to loose an error message text, the following command is # used to ensure that this output will be printed properly and in # particular without a "staircase effect" provided the error occurs on the # first page. printf '\033E\033&k2G\033&s0C' # PCL: Printer Reset, Line Termination (LF -> CR+LF), End-of-Line-Wrap (ON). # Now call ghostscript ("-dNOPAUSE -dBATCH" are currently superfluous, but # ghostscript's documentation is not sufficiently clear on this point to make # it appear safe to drop them). $gs_command $gs_options -dCUPSMessages $accounting -q -dNOPAUSE -dBATCH \ -dSAFER -sOutputFile=- $cfgfile - # Newer gs versions (>= 5.65) accept also "-_" which might be faster than "-". rc=$? else # Call ghostscript with an intermediate file. See also comments above. tmp="${TMPDIR:-/tmp}/$$.tmp" rm -f "$tmp" $gs_command $gs_options -dCUPSMessages $accounting -q -dNOPAUSE -dBATCH \ -dSAFER -sOutputFile="$tmp" $cfgfile - >&2 rc=$? # Copy the file to stdout on success test 0 -ne $rc -o ! -f "$tmp" || cat "$tmp" || { rm -f "$tmp" printf 'ERROR: %s: cat to stdout failed for %s.\n' "$name" "$tmp" >&2 exit 1 } rm -f "$tmp" fi if [ 0 -ne $rc ]; then printf 'ERROR: %s: ghostscript returned %s for job %s (invalid PostScript input?).\n' \ "$name" "$rc" "$job" >&2 # Send a mail message if we have mailx and "$user" is something sensible # (CUPS doesn't seem to guarantee that at present for remote users) if type mailx > /dev/null 2>&1 && \ { expr x"$user" : '.*@' > /dev/null || id "$user" > /dev/null 2>&1; } then mailx -s "$name"": Error printing job $job" "$user" << --- Ghostscript returned an error code ($rc) for your print job $job. The most likely cause is an invalid input file. If you submitted a PostScript file, run ghostscript or ghostview on it to check its correctness. If it is valid or you submitted a non-PostScript file, inspect CUPS' error log file for further information on the error. If that does not expose the cause, read the initial comments in $0, especially on DivertToFile. --- fi # No exit with non-zero return code because that would disable the printer. # As the error is most likely due to an illegal input file, such a drastic # step is not justified (it would even prevent other users from printing). fi exit 0