Using Your Own Filters to Print with CUPS

Support knowledgebase (jsmeix_print-cups-filters)
Applies to

SuSE Linux: Versions since 8.2

Situation

You want to use a custom filter to print with CUPS. Requirements: You should have a basic knowledge of the printing system and be well acquainted with CUPS, bash scripts, and common command-line tools.

Index:

  1. CUPS Filter System Background Information
  2. Application Examples of Custom Filters
  3. Printing Plain ASCII Text in Printer-Specific Code
  4. Optional PCL Printing on PostScript+PCL Printers
  5. PostScript Preprocessing with Ghostscript on PostScript Printers
  6. Restrictions

CUPS Filter System Background Information

The CUPS default filter system usually produces very good results and provides multiple possibilities for tuning the printing output to meet your requirements. However, custom filters might be needed in some exceptional cases.

Filter Activation Order:

CUPS default filter system works in general by way of the entries defined in /etc/cups/mime.types and /etc/cups/mime.convs. Where appropriate for each printing queue, the *cupsFilter entries in the PPD file are used.

Possibilities of Filter Option Settings:

  1. /usr/lib/cups/filter/texttops -- see CUPS Software User Manual: text options
  2. /usr/lib/cups/filter/pstops -- see CUPS Software User Manual: document options
  3. /usr/lib/cups/filter/foomatic-rip -- see CUPS Software User Manual: printer options

Application Examples of Custom Filters:

Printing Plain ASCII Text in Printer-Specific Code

You want to use a dot matrix printer to fill in forms with carbon copies on continuous stock. For this purpose, the dot matrix printer queue must forward plain ASCII text to the printer, but the character encoding must be customized to that of the printer.

Optional PCL Printing on PostScript+PCL Printers

PostScript printers are not able to print extremely complex PostScript documents. Especially if the printer is not equipped with sufficient memory, printing high resolution or color depth raster graphics embedded in the PostScript document may not be possible.

A 1200x1200 dpi bitmap graphic requires sixteen times as much memory as a 300x300 dpi one. 32-bit color depths need 32 times as much as a 1-bit black and white image. Thus, a 1200x1200 dpi bitmap graphic with a 32-bit color depth requires more than 500 times as much memory as the same graphic in 1-bit black and white with a 300x300 dpi resolution.

The direct solution is to reduce the resolution and color depth values in the application used to create the PostScript file. The printing system can convert problematic PostScript documents to PCL data (with a smaller resolution or color depth, if necessary) as described above. To be printed, PCL data requires (depending on the resolution and color depth) a printer with substantially less memory. PostScript printers usually can also be addressed via PCL. Most PostScript+PCL printers automatically detect the data type and switch back and forth between the PostScript and PCL mode.

The easiest approach consists of creating an additional queue for a PostScript+PCL printer. This queue always produces PCL data by using a PPD file for a compatible PCL printer.

The main disadvantage of using several queues for the same printer is that it is necessary to query all queues to display all waiting or active print jobs for that printer. Although not a problem for single printers or stand-alone systems, the use of several queues per printer might prove too complicated in the case of several network printers used by many users. For this reason, there should only be one queue for each PostScript+PCL printer that can alternatively produce PCL data to enable problematic PostScript documents to be printed via PCL (with a smaller resolution and color depth, if necessary).

PostScript Preprocessing with Ghostscript on PostScript Printers

Assuming that preprocessing with Ghostscript takes place via an additional queue and that yours is a PostScript+PCL printer, three queues for the same printer would be necessary (including the possibility of PCL printing). The disadvantages of having several queues per printer have been mentioned above. Thus, both PostScript preprocessing and PCL printing should be possible via a single queue.

Procedure for the Examples Above:

Printing Plain ASCII Text in Printer-Specific Encoding

  1. Create the queue for the dot matrix printer as usual with a Foomatic PPD file suitable for the printer model.

  2. Open the PPD file /etc/cups/ppd/queue.ppd and insert the line
    *cupsFilter: "text/plain 0 TextToPrinter"
    
    under the line
    *cupsFilter: "application/vnd.cups-postscript 0 foomatic-rip"

    The purpose of *cupsFilter entries in PPD files is the conversion to printer-specific data, which is subsequently sent to the printer. This particular entry directly converts all data with the MIME type text/plain to printer-specific data by way of /usr/lib/cups/filter/TextToPrinter, without the intervention of other filters. Therefore it is not possible to customize the printout when printing data with the MIME type text/plain in this queue because the necessary CUPS and Foomatic filters are missing.

  3. /usr/lib/cups/filter/TextToPrinter is the filter script that must be created for the conversion of ASCII text to printer-specific code. This script must be exactly tuned to the printer model.

    Many dot matrix printers include a character code compatible with IBM PC. You can use the command recode "lat1..ibmpc" to convert the ASCII text to IBM PC-compatible character code.

    If the printer is attached to the first parallel interface, use the following command

    echo -en "\rline 1\numlauts: ÄÖÜäöüß\nline 3\f" | recode "lat1..ibmpc" >/dev/lp0
    
    to test if recode "lat1..ibmpc" produces the right printer-specific code. If this is not the case, recode offers many more recoding possibilities. As a last resort, you can use (additionally) tr to convert single characters or sed to convert character strings. Refer to the relevant man pages.

    This script displays the printable characters along with their octal codes:

    #! /bin/bash
    # carriage return before printing
    echo -en "\r"
    # print printable 8-bit characters (CR, NL, TAB, BS, 32-126, 128-254)
    echo -en "the special characters horizontal tab and backspace:\r\n"
    echo -en "the next line consists of 5 horizontal tabs each followed by I\r\n"
    echo -en "\tI\tI\tI\tI\tI\r\n"
    echo -en "the next line consists of C backspace and =\r\n"
    echo -en "C\b=\r\n"
    echo -en "the printout of C backspace and = may look like an Euro sign\r\n"
    echo -en "\nthe printable 7-bit octal codes (040-176) and characters:\r\n"
    for i in 04 05 06 07 10 11 12 13 14 15 16
    do for j in 0 1 2 3 4 5 6 7
       do echo -en "${i}${j} \\${i}${j}  "
       done
       echo -en "\r\n"
    done
    for i in 170 171 172 173 174 175 176
    do echo -en "${i} \\${i}  "
    done
    if test "$1" = "7"
    then
    # form feed after printing
      echo -en "\r\f"
      exit 0
    fi
    echo -en "\r\n"
    if test "$1" = "a"
    then
      echo -en "\nthe 8-bit octal codes (200-237) and characters:\r\n"
      for i in 20 21 22 23
      do for j in 0 1 2 3 4 5 6 7
         do echo -en "${i}${j} \\${i}${j}  "
         done
         echo -en "\r\n"
      done
    fi
    echo -en "\nthe printable 8-bit octal codes (240-376) and characters:\r\n"
    for i in 24 25 26 27 30 31 32 33 34 35 36
    do for j in 0 1 2 3 4 5 6 7
       do echo -en "${i}${j} \\${i}${j}  "
       done
       echo -en "\r\n"
    done
    for i in 370 371 372 373 374 375 376
    do echo -en "${i} \\${i}  "
    done
    # form feed after printing
    echo -en "\r\f"
    
    Without entering any additional parameters, the output is the usually easy-to-print 8-bit code. By introducing 7 as a parameter, only printable 7-bit ASCII code will be produced. The parameter a produces all printable 8-bit code, but this may overwrite the terminal settings in the process. Thus, the parameter a is not suitable for display on screen.

    If the printer is attached to the first parallel interface, you can find out the required recoding by launching this script with a command similar to

    Script a >/dev/lp0
    
    The output will be the character set integrated in the printer. This way, the necessary character encoding can be exactly determined.

    If the recode command above produces the right printer-specific code, /usr/lib/cups/filter/TextToPrinter may look like this:

    #! /bin/bash
    # see http://localhost:631/spm.html#WRITING_FILTERS
    # debug info in /var/log/cups/error_log
    set -x
    # have the input at fd0 (stdin) in any case
    [ -n "$6" ] && exec <"$6"
    # carriage return before printing
    echo -en "\r"
    # printing
    recode "lat1..ibmpc"
    # form feed after printing
    echo -en "\f"
    

  4. The owner, group, and permissions of the filter script must match the rest of filters in /usr/lib/cups/filter/.

  5. Now reload or restart cupsd.

  6. Tests:

  7. Optional:

Optional PCL Printing on PostScript+PCL Printers

  1. Use the manufacturer PPD file for this particular printer model to create the queue for the PostScript+PCL printer. Alternatively, use the generic CUPS PPD file /usr/share/cups/model/Postscript.ppd.gz or the generic Foomatic PPD file /usr/share/cups/model/Generic/PostScript_Printer-Postscript.ppd.gz to operate the printer as a generic PostScript printer.

  2. If you have used the generic Foomatic PPD file, change /etc/cups/ppd/queue.ppd as follows:
    *cupsFilter: "application/postscript-problematic 0 PsToPCL"
    *cupsFilter: "application/vnd.cups-postscript 0 foomatic-rip"
    
    or insert the following lines in /etc/cups/ppd/queue.ppd as in the generic Foomatic PPD file:
    *cupsFilter: "application/postscript-problematic 0 PsToPCL"
    *cupsFilter: "application/vnd.cups-postscript 0 ToPrinter"
    
    The purpose of *cupsFilter entries in PPD files is the conversion to printer-specific data that is subsequently sent to the printer. This particular entry directly converts all data with the MIME type application/postscript-problematic to printer-specific data by way of /usr/lib/cups/filter/PsToPCL without the intervention of other filters. It is not possible to customize the printout when printing data with the MIME type application/postscript-problematic in this queue because the necessary CUPS and Foomatic filters are missing.

  3. Insert the new MIME type application/postscript-problematic as an additional line:
    application/postscript-problematic
    
    in /etc/cups/mime.types. Otherwise, CUPS will not accept this MIME type. Additionally, look at CUPS Software Administrators Manual: Adding Filetypes and Filters.

  4. /usr/lib/cups/filter/PsToPCL is the filter script that must be created for the conversion of PostScript to printer-specific code. This script must be exactly tuned to the printer model.

    Most PostScript+PCL printers understand the print language PCL5e, which is produced by the Ghostscript drivers ljet4 and lj4dith for Floyd-Steinberg dithering and ljet4d for duplex printing with resolutions up to 600 dpi. If the printer is attached to the first parallel interface, use the following Ghostscript command to test whether ljet4 (or other PCL5e drivers) produces the right printer-specific code.

    gs -q -dBATCH -dNOPAUSE -sDEVICE=ljet4 -sOutputFile=/dev/lp0 /usr/share/doc/packages/ghostscript/examples/colorcir.ps
    
    If this is not the case, Ghostscript offers additional PCL drivers (see the SDB article "Purchase of Printers and Compatibility" (http://sdb.suse.de/en/sdb/html/jsmeix_print-kompatibel.html)).

    If the color ellipse has been properly printed with the command gs mentioned above, the filter /usr/lib/cups/filter/PsToPCL to convert data with the MIME type application/postscript-problematic with ljet4 to PCL5e with a resolution of 300 dpi might look like this:

    #! /bin/bash
    # see http://localhost:631/spm.html#WRITING_FILTERS
    # debug info in /var/log/cups/error_log
    set -x
    # have the input at fd0 (stdin) in any case
    [ -n "$6" ] && exec <"$6"
    # printing
    gs -q -dBATCH -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ljet4 -r300 -sOutputFile=- -
    

  5. All other data has the MIME type application/vnd.cups-postscript and will be either processed with /usr/lib/cups/filter/foomatic-rip or directly sent to the printer by /usr/lib/cups/filter/ToPrinter. /usr/lib/cups/filter/ToPrinter may be similar to:
    #! /bin/bash
    # see http://localhost:631/spm.html#WRITING_FILTERS
    # debug info in /var/log/cups/error_log
    set -x
    # have the input at fd0 (stdin) in any case
    [ -n "$6" ] && exec <"$6"
    # printing
    cat -
    

  6. The owner, group, and permissions of the filter scripts must match the rest of filters in /usr/lib/cups/filter/.

  7. Now reload or restart cupsd.

  8. The CUPS option
    -o document-format=application/postscript-problematic
    
    enables you to set the MIME type of the PostScript document to print to application/postscript-problematic on the command line. Depending on whether this option is set, data will be processed with the specific filter /usr/lib/cups/filter/PsToPCL or with the common CUPS filters. Because Ghostscript only accepts PostScript data as input from stdin, the MIME type application/postscript-problematic can only be used for PostScript documents.

    You can print a PostScript test page with /usr/lib/cups/filter/PsToPCL with the command:

    lp -d queue -o document-format=application/postscript-problematic /usr/share/doc/packages/ghostscript/examples/colorcir.ps
    
    As a result, /var/log/cups/error_log contains:
    I ... Started filter /usr/lib/cups/filter/PsToPCL
    ...
    D ... + '[' -n /var/spool/cups/... ']'
    D ... + exec
    D ... + gs -q -dBATCH -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ljet4 -r300 -sOutputFile=- -
    

  9. Optional:

  10. Printing problematic PDF documents as PCL:

    Ghostscript can also handle PDF documents directly, but only if they have been received as ordinary files instead of via stdin.

    According to CUPS Software Programmers Manual: Writing Filters: Command-Line Arguments, only the first filter in the chain receives its input from CUPS spool file and /usr/lib/cups/filter/PsToPCL is the only filter. Therefore, this filter can be changed as follows so Ghostscript can receive its input directly from CUPS spool file:

    #! /bin/bash   
    # see http://localhost:631/spm.html#WRITING_FILTERS
    # debug info in /var/log/cups/error_log
    set -x
    # set inputfile to where the input comes from
    inputfile="-"
    [ -n "$6" ] && inputfile="$6"
    # printing
    gs -q -dBATCH -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ljet4 -r300 -sOutputFile=- $inputfile
    

  11. Switching from PostScript to PCL with PJL commands:

    Many PostScript+PCL printers understand the job control language PJL. If your printer does not switch automatically from PostScript to PCL but supports PJL, the switch can be forced with PJL commands. /usr/lib/cups/filter/PsToPCL can be expanded to enable the corresponding PJL commands to be sent before and after the PCL data:

    #! /bin/bash
    # see http://localhost:631/spm.html#WRITING_FILTERS
    # debug info in /var/log/cups/error_log
    set -x
    # set inputfile to where the input comes from
    inputfile="-"
    [ -n "$6" ] && inputfile="$6"
    # switch to PCL and do a PCL reset
    echo -en "\033%-12345X@PJL ENTER LANGUAGE = PCL\n\033E"
    # printing
    gs -q -dBATCH -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ljet4 -r300 -sOutputFile=- $inputfile
    # PCL reset and PJL end of job
    echo -en "\033E\033%-12345X\n"
    
    In the same way, control commands in job control languages other than PJL can be sent to the printer.

  12. Switching from PCL to PostScript with PJL commands:

    After having switched from PostScript to PCL with PJL commands, the final PJL string \033%-12345X should switch the printer back to its original PostScript mode. To force the switch from PCL to PostScript with PJL commands, expand /usr/lib/cups/filter/ToPrinter as follows:

    #! /bin/bash
    # see http://localhost:631/spm.html#WRITING_FILTERS
    # debug info in /var/log/cups/error_log
    set -x
    # have the input at fd0 (stdin) in any case
    [ -n "$6" ] && exec <"$6"
    # switch to PostScript
    echo -en "\033%-12345X@PJL ENTER LANGUAGE = POSTSCRIPT\n"
    # printing
    cat -
    # PostScript end of transmission and PJL end of job
    echo -en "\004\033%-12345X\n"
    
    If a queue has been created with the printer-specific PPD file of the manufacturer, the corresponding job control commands should be included in the PPD (as *JCL... entries). CUPS automatically sends these commands to the printer and, thus, any additional job control commands will not be required.

  13. Setting particular printing options with PJL commands when printing in PCL mode:

    If your printer understands PCL and PJL, you can use PJL commands to activate some printing options.

    The syntax of a PJL+PCL print job is:

    \033%-12345X@PJL SET option-1 = value-1
    @PJL SET option-2 = value-2
    ...
    @PJL SET option-n = value-n
    @PJL ENTER LANGUAGE = PCL
    PCL-commands+data\033%-12345X
    
    Every PJL print job must begin and end with exactly one "Universal Exit Language" command \033%-12345X. Every PJL line must end with exactly one linefeed character \n. Possible options and their values depend on the printer model.

    If the printer is attached to the first parallel interface, use the following command to test if the PJL commands work as requested. This example activates the toner economy mode and the manual confirmation before printing (e.g., to be able to insert special paper):

    echo -en "\033%-12345X@PJL SET ECONOMODE = ON\n@PJL SET MANUALFEED = ON\n@PJL ENTER LANGUAGE = PCL\n" >/dev/lp0
    
    gs -q -dBATCH -dNOPAUSE -sDEVICE=ljet4 -sOutputFile=/dev/lp0 /usr/share/doc/packages/ghostscript/examples/colorcir.ps
    
    echo -en "\033%-12345X\n" >/dev/lp0
    
    Expand /usr/lib/cups/filter/PsToPCL as follows to send the corresponding PJL commands:
    #! /bin/bash
    # see http://localhost:631/spm.html#WRITING_FILTERS
    # debug info in /var/log/cups/error_log
    set -x
    # set inputfile to where the input comes from
    inputfile="-"
    [ -n "$6" ] && inputfile="$6"
    # PJL printer setup
    echo -en "\033%-12345X@PJL SET ECONOMODE = ON\n@PJL SET MANUALFEED = ON\n"
    # switch to PCL and do a PCL reset
    echo -en "@PJL ENTER LANGUAGE = PCL\n\033E"
    # printing
    gs -q -dBATCH -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ljet4 -r300 -sOutputFile=- $inputfile
    # PCL reset and PJL end of job
    echo -en "\033E\033%-12345X\n"
    

PostScript Preprocessing with Ghostscript on PostScript Printers

  1. Use the manufacturer PPD file for this particular printer model to create the queue for the PostScript printer or use the generic CUPS or Foomatic PPD files to operate the printer as a generic PostScript printer (see above).

  2. Insert the new MIME type application/postscript-pswrite as an additional line
    application/postscript-pswrite
    
    in /etc/cups/mime.types. Additionally, have a look at CUPS Software Administrators Manual: Adding Filetypes and Filters.

  3. Now insert the line
    application/postscript-pswrite application/postscript 33 PsWrite
    
    in /etc/cups/mime.convs. Additionally, have a look at CUPS Software Administrators Manual: Adding Filetypes and Filters.

    By doing this, all data with the MIME type application/postscript-pswrite will be converted to data with the MIME type application/postscript by means of /usr/lib/cups/filter/PsWrite. This filter is available for all queues.

    The printout of data with the MIME type application/postscript-pswrite can be customized to a large extent because data with the MIME type application/postscript goes through the common CUPS and Foomatic filters. However, the possibilities of /usr/lib/cups/filter/pstops according to
    CUPS Software Users Manual: Document Options do not always work in connection with PostScript preprocessing. Nevertheless, at least the printer-specific options according to the PPD file should work.

  4. The Ghostscript driver pswrite is used for PostScript preprocessing with Ghostscript. If the printer is attached to the first parallel interface, use the following Ghostscript commands
    gs -q -dBATCH -dNOPAUSE -sDEVICE=pswrite -sOutputFile=/dev/lp0 /usr/share/doc/packages/ghostscript/examples/colorcir.ps
    
    gs -q -dBATCH -dNOPAUSE -sDEVICE=pswrite -dLanguageLevel=2 -sOutputFile=/dev/lp0 /usr/share/doc/packages/ghostscript/examples/colorcir.ps
    
    gs -q -dBATCH -dNOPAUSE -sDEVICE=pswrite -dLanguageLevel=1 -sOutputFile=/dev/lp0 /usr/share/doc/packages/ghostscript/examples/colorcir.ps
    
    to find out which LanguageLevel produces the right printer-specific data. If the printer is equipped with PostScript level 1, the filter /usr/lib/cups/filter/PsWrite may be similar to
    #! /bin/bash
    # see http://localhost:631/spm.html#WRITING_FILTERS
    # debug info in /var/log/cups/error_log
    set -x
    # set inputfile to where the input comes from
    inputfile="-"
    [ -n "$6" ] && inputfile="$6"
    # printing
    gs -q -dBATCH -dPARANOIDSAFER -dNOPAUSE -sDEVICE=pswrite -dLanguageLevel=1 -sOutputFile=- $inputfile
    

  5. The owner, group, and permissions of the filter script must match the rest of filters in /usr/lib/cups/filter/.

  6. Now reload or restart cupsd.

  7. The CUPS option
    -o document-format=application/postscript-pswrite
    
    enables you to set the MIME type of the PostScript document to print to application/postscript-pswrite on the command line. Depending on whether this option is set, data will be preprocessed with the specific filter /usr/lib/cups/filter/PsWrite or only the common CUPS filtering is done. Because /usr/lib/cups/filter/PsWrite is the first filter in the chain, it receives its input directly from CUPS spool file as described in CUPS Software Programmers Manual: Writing Filters: Command-Line Arguments. Thus, preprocessing PDF documents is possible, too.

    You can print a PostScript test page with /usr/lib/cups/filter/PsWrite with the command:

    lp -d queue -o document-format=application/postscript-pswrite /usr/share/doc/packages/ghostscript/examples/colorcir.ps
    
    As a result, /var/log/cups/error_log contains:
    I ... Started filter /usr/lib/cups/filter/PsWrite
    I ... Started filter /usr/lib/cups/filter/pstops
    ...
    D ... + gs -q -dBATCH -dPARANOIDSAFER -dNOPAUSE -sDEVICE=pswrite -dLanguageLevel=1 -sOutputFile=- /var/spool/cups/...
    

  8. Optional:

Restrictions:

Neither optional PCL printing on PostScript+PCL printers nor PostScript preprocessing on PostScript printers should be considered as a panacea for printing any PostScript or PDF document.

In any case, Ghostscript must be able to process the PostScript or PDF document. If both the printer's PostScript interpreter and Ghostscript fail to process the document, it is probably so faulty that it is impossible to print.

The attempt to print a particular document from an application might fail because the application's PostScript output cannot be completely processed by the printing system (or not at all). This can be verified by printing from the application used to create the problematic PostScript or PDF document to a PostScript file. Alternatively, you can also take the existing PostScript or PDF document and display it page by page on your graphical interface by executing

gs -r60 file_name
in a terminal and confirming with the enter key. To close the displayed document, press the key combination [Ctrl]+[C] in the terminal. If the PostScript or PDF file is not properly displayed on a second window or Ghostscript error messages are displayed in the terminal where the gs command has been executed, Ghostscript is not able to process the application's PostScript output.

The problem is sometimes caused by special characters or fonts that cannot be produced in PostScript by the application in such a way that enables Ghostscript or the printer to process them. The problem can be avoided by not using these characters or fonts.


Keywords: PRINT, PRINTER, CUPS, FILTER, POSTSCRIPT, PDF

Categories: Frequently asked Questions , Documentation , Printer

Feedback welcome: Send Mail to jsmeix@suse.de (Please give the following subject: SDB-jsmeix_print-cups-filters)
SDB-jsmeix_print-cups-filters, Copyright SuSE Linux AG, Nürnberg, Germany - Version: 04. Jun 2003
SuSE Linux AG - Last generated: 23. Jul 2003 by ip (sdb_gen 1.40.0)