Tech Notes

Tech Note #20130003

The Common Unix Printing System (CUPS) is capable of handling a wide variety of file and document types (a.k.a. MIME types) and printing them on a large selection of printers. In order to be this useful and flexible, CUPS employs a number of configuration files, filters and device drivers which direct it in its handling of files/documents and printers.

However, when a file is spooled to a printer and it fails to print, one is faced with a large number of possible reasons for the failure. CUPS logging can certainly be turned on but study of the log file often does little to shed light on the problem. In that case, resorting to a methodological approach to troubleshooting the problem can lead to a positive outcome, i.e. a printer that prints.

Problem Description:

When a CUPS printer fails to print anything or perhaps it will print some files or documents but not others, one is faced with a troubleshooting problem that is quite complex. The problem could be due to the printer being improperly configured. Or, the file/document type may be misconfigured. If the configuration is correct, the filter chain could be incorrect or broken. And, if all of those things are fine, the device driver itself can be missing or not working.

How does one go about discovering which of these parts of the whole picture are not working? The CUPS logfile is often less than helpful -- more often than not, it is concerned with the spooling and scheduling of print jobs, not why they go wrong.

Problem Resolution:

This note describes a series of troubleshooting steps, to be carried out in order, that will generally lead to resolution of printing problems. A large part of resolving printing problems hinges on a thorough understanding of the printing process itself, so this note also includes a modicum of CUPS printing theory to assist in shedding light on what can go wrong.

The first step in fixing printing troubles is to ensure that the printer is properly configured. This done through a file called a PPD (PostScript Printer Description), whose job is to inform CUPS about all of the needs and quirks of the printer, specifically what kinds of control sequences are used, what kinds of file/document types can be printed (e.g. postscript, PCL), what options are available and how they can be set (e.g. available fonts built into the printer, duplexing capabilities, media sizes handled, non-printable margins, available resolutions), what status codes are presented and what they mean, and any other protocols that should be used to talk to the printer and tell it what to do.

One can obtain a PPD from many places. The printer manufacturer may supply one on their software disk or site. You may find one on a third party site. You might crib one from an existing installation or even another OS (e.g. Windows can be a source of PPDs for CUPS printers on Linux). You can use a generic PPD (e.g. generic postscript) or you can even make one up yourself (PPDs are simple text files so your favorite text editor is your friend in this respect). Our suggestion for a good place to start is the CUPS Drivers page. Another good place to look is The Linux Foundation OpenPrinting Project Printer Driver Listings. If you want the nitty gritty details about PPDs, you can find them in the Adobe PostScript Printer Description File Format Specification but reading this specification is generally not necessary unless you really feel the need to customize or create a PPD.

When a printer is set up (usually from the CUPS Adding Printers And Classes Administration page of the Web UI), the PPD that is added as a result of this step is copied to the CUPS printer configuration directory (typically /etc/cups/ppd) and given a name that matches the printer's name. Thus, for example, if you were to set up a printer named "MyLaser", you would expect to find its PPD in "/etc/cups/ppd/MyLaser.ppd".

To begin your troubleshooting, check that the printer's PPD exists in the configuration directory. Open it up with your favorite text editor and verify that it is the proper PPD for your printer. If it isn't, delete the printer and reinstall it with the correct PPD.

One can turn on general CUPS tracing, to cause CUPS to write information to the /var/log/cups/error_log file as it goes about its business. We don't find this information to be too useful but you may wish to look at it anyway. To turn it on, enter these commands:

su
cupsctl LogLevel=debug2

When done, one can turn off general CUPS tracing by entering these commands:

su
cupsctl --no-debug-logging

One word of caution, however, particularly if you have annotated your cupsd.conf file in /etc/cups with useful comments. The cupsctl commands shown above alter the cupsd.conf file and, in the process, wipe out any comments therein. Instead of using the cupsctl command, you can edit /etc/cups/cupsd.conf directly and set:

LogLevel debug2

To turn off debugging, set:

LogLevel info

If printing with CUPS tracing turned on fails to give you any useful information, the next step is to make sure that a good test page is installed on your system. By good, we mean one that works with your printer and that can be printed by your CUPS installation. You might want to take a look at our Tech Note Tech_20130002 on the subject of "Printing Test/Banner Pages With CUPS", to see what things can go wrong with CUPS printer test pages and how to fix them.

On later CUPS installations (e.g. 1.6 and greater), our preference is to revert to an old, postscript-based test page testprint-1.6.ps.gz that we have provided for download. We install this test page into the /usr/share/cups/data directory, replacing the existing file named "testprint". You can overwrite it or you can rename it to something like "testprint.pdf" to keep it around for posterity. Unzip the downloaded file and copy it to the directory, renaming it on the way:

su
mv /usr/share/cups/data/testprint /usr/share/cups/data/testprint.pdf
gunzip -c testprint-1.6.ps.gz >/usr/share/cups/data/testprint

At this point, the printer test page can be sent to the printer to see if it is working:

lpr -P MyLaser /usr/share/cups/data/testprint

It is a good bet that the printer test page won't appear on the printer. If it does, the problem was with the new printer test page (it hangs some earlier postscript printers) or the PPD file. If so, you're in luck.

If the print job shows an error that reads: Unsupported format "application/vnd.cups-banner", or some other "unsupported format" type error, the problem is with the MIME types and MIME conversion configuration files.

These two files are installed into either /etc/cups (early versions of CUPS) or /usr/share/cups/mime (later versions of CUPS such as 1.6) and are named mime.types and mime.convs (in later versions of CUPS, you may also install local configuration files with the extensions .types and .convs in the /etc/cups directory, which override or augment mime.types and mime.convs). The operation of the .types file(s) is to tell CUPS which file types can be printed and, additionally, provide rules that CUPS can use to parse a file to automatically determine its type. The key point is that, if a file's type isn't in either one or both .types files, CUPS will return an "unsupported format" type error,

If you are using the printer test page that we suggested above, you should look for some lines in the configuration file that look like:


########################################################################        
#                                                                               
# Application-generated files...                                                
#                                                                               

application/postscript      ai eps ps string(0,%!) string(0,<04>%!) \
                            contains(0,128,<1B>%-12345X) + \
                            (contains(0,4096,"LANGUAGE=POSTSCRIPT") \
                             contains(0,4096,"LANGUAGE = Postscript") \
                             contains(0,4096,"LANGUAGE = PostScript") \
                             contains(0,4096,"LANGUAGE = POSTSCRIPT") \
                             (contains(0,4096,<0a>%!) + \
                              !contains(0,4096,"ENTER LANGUAGE")))

If these lines aren't present, the postscript printer test page will never print. On the other hand, if you are using the PDF test/banner pages that come with the latest version of CUPS (e.g. 1.6), make sure there are lines in the configuration file that look like:


########################################################################
#
# Banner types...
#

application/vnd.cups-pdf-banner     string(0,'#PDF-BANNER')

In either case, the lines in the mime.types file use the pattern matching feature of this configuration file to look inside the printer test page and determine what file type it is (either postscript or vnd.cups-pdf-banner). Once the file type is determined, at the same time that it tells CUPS that it should print the file, it also tells CUPS what filters to use in printing the file.

One should then examine the mime.convs file in the /usr/share/cups/mime directory and any local .convs files in the /etc/cups directory. On later CUPS installations (e.g. CUPS 1.6), we use a local.convs file that looks like this:


#
#   Local MIME conversions file for CUPS Filters.
#
#   This file describes how to convert files of one MIME type to files
#   of another.
#

########################################################################
#
# Text filters...
#

application/x-cshell    application/pdf              0    texttopdf
application/x-csource   application/pdf              0    texttopdf
application/x-perl      application/pdf              0    texttopdf
application/x-shell     application/pdf              0    texttopdf
text/plain              application/pdf              0    texttopdf
text/html               application/pdf              0    texttopdf

########################################################################
#
# PDF filters
#

application/pdf   application/postscript             66   pdftops

########################################################################
#
# Banner types...
#

application/vnd.cups-pdf-banner   application/pdf    33   bannertopdf

The job of this configuration file is to tell CUPS what filters to apply to a print file of a particular type to permute it into a print file that can be sent to the printer. Each line in this configuration file describes a single filter operation and, thus, a single link in the chain.

Perhaps an example would serve to illustrate better. Let's assume the printer requires postscript files (this requirement is specified in the PPD) of a type application/vnd.cups-postscript. And, let's assume that we're using the new PDF banner/test pages that map to MIME type application/vnd.cups-pdf-banner.

CUPS looks in the local.convs configuration file and finds the rule that maps application/vnd.cups-pdf-banner to application/pdf using the bannertopdf filter. It then looks in the local.convs configuration file and finds the rule that maps application/pdf to application/postscript using the pdftops filter. Now, CUPS looks in the mime.convs file and sees that application/postscript needs to be passed through the pstops filter to convert it into the application/vnd.cups-postscript MIME type. Bingo, this is the print file type that the printer requires. CUPS now knows that it should pass the printer test page through the bannertopdf filter, followed by the pdftops, followed by the pstops filter before sending it to the printer.

If we were to print the postscript test page instead, inspection of the mime.convs file would indicate that the printer test page with MIME type application/postscript (from the rule in mime.types) needs to be passed through the pstops filter to convert it into the application/vnd.cups-postscript MIME type that can be sent to the printer.

Once you have inspected the MIME types and conversions configuration files, you can test that the filter chain works for any type of file using the cupsfilter test program like this:

cupsfilter -m printer/foo -p /etc/cups/ppd/MyLaser.ppd \
  /usr/share/cups/data/testprint >/home/joeblow/testprint-ps.prt

In this case, we're sending the postscript printer test page through the filter chain specified for the printer defined by the MyLaser PPD. The output MIME type of "printer/foo" indicates that cupsfilter should look up the required MIME type for the printer chosen and apply all of the necessary filters in the chain to convert the print file into the MIME type required by the printer.

This will show you, via debug statements in the filters, all of the filter steps taken and, if there are any conversion problems, what they are. You should correct any missing conversion steps by editing the MIME conversion configuration file(s), or other errors that are noted. The filter chain can be validated for any input file type that you expect to print. Once the filter chain is working for all of the file types that you expect to print, you can move on to the next step. You'll need one of the files generated by this step.

If a filter that is configured in the filter chain which is used to print a particular MIME file type, is missing from the system, any attempts to print this type of file will receive one of the ubiquitous "unsupported format" type errors. You can check that all of the filters needed are present by examining /usr/lib/cups/filter.

If a required filter isn't installed in the /usr/lib/cups/filter directory, you should either build the filter under the CUPS build tree or build it under the cups-filters tree. If you need a filter that is found in the cups-filters tree, download the latest cups-filters from The Linux Foundation OpenPrinting Project:

http://www.openprinting.org/download/cups-filters/

Unpack the build files:

tar -xvjf cups-filters-1.0.34.tar.bz2

On certain operating systems (e.g. CentOS 6.3), building the filters is a real goat rope, since the prerequisites for the build libraries are way too high for the usual, out of date stuff that RedHat uses. However, it is possible to get by with what's there by bypassing pkg-config.

Begin by finding the libraries and link options to use when building modules that use glib-2.0, IJS and poppler:

pkg-config --libs --cflags glib-2.0
ijs-config --libs --cflags
pkg-config --libs --cflags poppler

The results of these commands are dropped into the configure command:

cd cups-filters-1.0.34
GLIB_CFLAGS="-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include" \
  GLIB_LIBS=-lglib-2.0 IJS_CFLAGS=-I/usr/include/ijs IJS_LIBS=-lijs \
  POPPLER_CFLAGS=-I/usr/include/poppler POPPLER_LIBS=-lpoppler \
  LIBQPDF_CFLAGS=-I/usr/local/include/qpdf LIBQPDF_LIBS=-lqpdf \
  ./configure

If you don't have all of the prerequisites (i.e. the latest QPDF and poppler), you can fix that situation like so (almost all systems will come with glib-2.0 and IJS already installed).

Since QPDF is used to do all of the heavy lifting in the new PDF workflow scheme of things, you will need to obtain the latest one (older versions generally won't do) from:

http://sourceforge.net/projects/qpdf/files/qpdf/

Extract and build it:

tar -xvzf qpdf-4.1.0.tar.gz
cd qpdf-4.1.0
./configure
su
make install
/sbin/ldconfig

In the case of poppler, our friends at Redhat/CentOS have decided that to install a number of header files in /usr/include, you need to install a whole raft of extra junk, including but not limited to lynx. You've got to be kidding.

If you don't care or you use some other OS, you can just go ahead and slap poppler on using the system's package manager but, if you do care, the solution to this little quandry is to download the RPM by hand from:

http://pkgs.org/centos-6-rhel-6/centos-rhel-i386/\
  poppler-devel-0.12.4-3.el6_0.1.i686.rpm/download/

Make sure you click on the correct "download" link because there are a lot of advertisemens on that page that say "Download" but they ain't what you want.

Install the RPM by hand (look Ma, no lynx):

su
rpm -i --nodeps poppler-devel-0.12.4-3.el6_0.1.i686.rpm

Now, we should be good to go but, since the prerequisites aren't actually met, it is not possible to go ahead with a normal build. Instead we just build the pieces that we need:

cd cups-filters-1.0.34
make bannertopdf
make pdftops

The install won't work either but we can install the filter modules by hand:

su
cp bannertopdf /usr/lib/cups/filter
cp pdftops /usr/lib/cups/filter
cp filter/texttops /usr/lib/cups/filter
chmod u=rwx,go=rx /usr/lib/cups/filter/*

Note that, at this point, in order to print text files directly from the print server, one needs to take several additional steps that require the installation of character sets and fonts. We have outlined these steps in our note Tech_20130004. If you are planning to print text files, you should read that note now and follow the instructions therein before proceeding.

Furthermore, there can be problems with the postscript that is generated by the standard filter chain (i.e. pdftops) when it is sent to older postscript printers, for example by causing the printer to crash. And, in some cases, the print files that are generated are so large (e.g. 35MB for the printer test page) that it looks like the printer has crashed, although it eventually prints the page, if you wait long enough. Given this situation, we have written a set of notes, found in Tech_20130005, about how to make sure that PDF and text files print properly on older printers.

If you have an older printer that doesn't print the postscript that is generated by the standard filter chain (typically, the problems manifest themselves by the printer simply hanging in the middle of a print job but they can also show up as improperly rendered output), you should read the notes in Tech_20130005 and follow the instructions therein to set things right before proceeding.

Once the print file filters are all installed, the filter chain properly configured, and we can produce a converted print file (in our case, the printer test page) from cupsfilter, thereby proving that the filter chain is working, it is time to test the backend portion of CUPS.

All filtered print files are sent to the printer by a backend driver program. The backend driver program that is used to talk to the a particular printer depends on the printer communications protocol chosen when the printer was set up. If the printer was configured as a LPD printer, by setting its URI to "lpd://hostname/queue", the backend program will be lpd (all backends are found in /usr/share/cups/backend). If the URI is "http://hostname:631/ipp/" or "ipp://hostname/ipp/", the backend will be ipp. For URIs like "socket://hostname:9100", the backend will be socket. More information on this subject is available from the CUPS Web UI by clicking on the Online Help tab and picking the Using Network Printers topic. Samba or Windows printers that use URIs like "smb://server/printer" use the smb backend which is actually a symlink that points to smbspool, a program provided by Samba. You can use man smbspool to find out more about smbspool.

As an aside, the symlink to smbspool may be incorrect, especially if you've installed Samba from source. CUPS uses a symlink that points to "../../../bin/smbspool" which typically resolves to "/usr/bin/smbspool". If the directory structure gets changed, this relative symlink may end up pointing to the wrong place so it is worth giving it a check.

Also, if you need either the serial or parallel backend program, you will need to build it under the cups-filters tree, which must be downloaded from The Linux Foundation OpenPrinting Project (see the notes in the previous paragraphs about how to build filters in the cups-filters tree. At the appropriate point, do this:

cd cups-filters-1.0.34
make serial
make parallel
su
cp serial /usr/lib/cups/backend
cp parallel /usr/lib/cups/backend
chmod u=rwx,go=rx /usr/lib/cups/backend/serial \
  /usr/lib/cups/backend/parallel

Knowing the backend driver program that CUPS uses to send output to the printer allows us to test that component to see that it is working properly. The following commands should send the printer test page, that we converted in the previous step with the cupsfilter program, to a LPD printer:

su
DEVICE_URI="lpd://hostname/MyLaser" /usr/lib/cups/backend/lpd \
  1234 joeblow "Test lpd backend" 1 "" \
  /home/joeblow/testprint-ps.prt

Printers driven by the ipp or socket backends can be tested in a similar fashion. For printers driven by the smb backend, we test the smbspool program directly like this:

su
DEVICE_URI="smb://server/MyLaser" /usr/bin/smbspool \
  1234 joeblow "remotefile" 1 "" \
  /home/joeblow/testprint-ps.prt

Running the printer backend programs in this fashion should produce debugging output that will assist you with troubleshooting any printer communication problems. If you still can't figure out what is going on, using a packet sniffer like WireShark to view the server/printer packet traffic may prove enlightning.

We've had plenty of problems with the LPD protocol so we rewrote the CUPS 1.6.2 version of the lpd backend program to produce much more debugging output with respect to timeouts and other communication errors. If you'd like a copy of the altered source, that you can drop in to your build tree, you can download lpd-1.6.2.c.gz.

By treating each piece of the CUPS printing path as a separate component and debugging each one in turn, one can find many problems that can block printing and resolve them to get all of your printers humming along. You may want to test all of the file types that you plan on printing to ensure that they are all working too.