User Tools

Site Tools



Designing, Writing and Packaging Printer Drivers

This little HOWTO is intended to help everyone who wants to write printer drivers for Linux and Unix, as printer manufacturers, ISVs, and also users who simply want to get their printers to work. The goal is to make the drivers available in a form that they “just work” for as many users as possible and that they do not require deep knowledge in system administration and/or software development for installation.

Printer driver design

To reach this goal one should already start at the design of the driver. This makes the later steps, as building in the LSB environment and packaging much easier.

Components and purposes

The printing process under Linux and Unix is as follows: Applications send the print job as a PDF (PostScript is deprecated but there can be some still sending PostScript) file to the printing system (usually CUPS) and the printing system converts the job to the printer's language. The resulting data stream it sends through a printer port to the printer.

In the simplest case the printer driver is simply what converts PDF (in former times PostScript) into the printer's language, but often there are several other components.

PPD files: The PostScript Printer Description (PPD) files are the only absolutely required part of the printer driver. The PPDs describe the printer's capabilities, the accepted input data formats (standard formats which CUPS can generate, preferably CUPS Raster and PDF), if needed driver executables (renderer/filter) to turn the input into the printer's native language,  printer-specific options and settings to control the print output, and how these options are presented in user interfaces, like printing dialogs and printer setup tools. CUPS knows which driver to use for a printer by its PPD file repository. So for an installed printer driver package to be used by CUPS, the package has to add its PPD files, one for each supported printer model, to CUPS' PPD file repository.

The PPD files have to follow at least Adobe's PPD specifications in order to work with CUPS, but to integrate perfectly with the Linux/CUPS environment they should also use the PPD extensions for CUPS and for the Common Printing Dialog. The PPD extensions for the Common Printing Dialog give you also advanced possibilities like branding (icon of manufacturer logo, picture of printer, …), icons/pictures for options and settings, specifying widget types and data types for options, …

Note that PPD files were originally designed as PostScript Printer Description files and that they are still used as such for native PostScript printers, but in the age of the PDF-centric printing workflow they are still very useful as a kind of universal printer description file or CUPS printer description file. For backward compatibility PPD files without “*cupsFilter” lines will still default to take PostScript as input and pass it through to the printer, but as PostScript is deprecated as print job format, such PPD files should only be used for native PostScript printers (where then a pdftops filter will kick in as “PostScript printer driver”). For non-PostScript printers PPDs should always have at least one “*cupsFilter” line and never take only PostScript as input format. If PostScript is taken in addition to other formats, PostScript should have a cost factor by 100 higher than the highest factor of the other formats.

Renderer/filter: This is the core part of the drivers for printers which do not support a standard page description language (PDL). It converts a device-independent PDL from standard input into the (model-dependent) PDL of the printer on standard output.

In the beginning Unix was used only in computing centers where one had only PostScript and text-only printers. Data was simply sent without special filtering. Later Ghostscript was introduced as a software PostScript interpreter (currently it also renders PDF and this is the preferred way to use it) to support non-PostScript printers by converting PostScript into the appropriate language. The drivers for the printer languages were part of the monolithic Ghostscript executable. To add a driver one had to patch and recompile Ghostscript.

With the time several forms of modularized drivers appeared. This way the renderer (currently only Ghostscript) and the filter got separate and so recompiling Ghostscript was not necessary any more. This simplified adding a printer driver a lot. Currently the recommended (and required by the LSB 3.2) printer driver interfaces in Ghostscript are:

CUPS Raster

CUPS raster drivers are a special form of filter type drivers. The concept is developed as a part of the CUPS printing system to make driver installation easy. CUPS calls Ghostscript to translate the PDF input into the CUPS raster format and an additional CUPS filter to translate the CUPS raster format into the printer's language. These drivers come always with PPDs which do not only contain options and printer properties but also instruct CUPS to call the correct CUPS filter. Which of these drivers are installed you see by the PPD files in /usr/share/ppd (and its subdirectories) and by the printer models showing up in the lists when setting up a print queue with the web interface of CUPS (entries usually contain “CUPS”).

IJS plug-in

IJS plug-ins are also separate executables and so they also do not require Ghostscript to be patched and recompiled to add support for a new printer. But in contrary to CUPS raster drivers IJS plug-ins communicate bi-directionally with Ghostscript and so Ghostscript can ask them for certain printer properties and adapt its rendering appropriately. The executables have to be in the execution path (or called with an absolute path). They usually contain “ijs” somewhere in their names and some of them can be called with the “-h” command line option to get version information.

OpenPrinting Vector

This interface is developed by the Japanese subgroup of the OpenPrinting working group and it is the only one which allows to plug vector(high-level drawing)-based printer drivers into Ghostscript. The connection between renderer and driver can be done by dynamic library linking or by communicating with a separate driver process via RPC. The latter method helps to avoid license conflicts and also makes it easier to provide distribution-independent driver packages.

With all these interfaces in place there is no necessity any more to write printer drivers which have to be compiled into Ghostscript. This method is deprecated and should not be used any more.

Currently, Ghostscript is the only PDF (formerly PostScript) renderer for printing. This may change in the future, especially as the standard format of print jobs is PDF and not Postscript any more. For example there could be XPDF/Poppler-based renderers with the above-mentioned driver interfaces. This is another reason to not write printer drivers to be compiled into Ghostscript any more.

Low-level communication with the printer: Usually nothing special is needed for the low-level communication with the printer. The printers have standard parallel, USB, and or network interfaces and you can simply feed in the data (in a language which the printer understands) and the printer prints.

In some cases special software is needed to communicate with the printer:

  • Bi-directional communication with the printer, to observe the ink/toner levels, clean or align the printing mechanism, auto-configure the printer, …
  • Access to fax, scanner, or other extra features of a multi-function device
  • The printer does not work with the standard communication protocols due to a hardware bug
  • The printer has a completely non-standard communication protocol

This software should not do the conversion of PDF into the printer's language. It should simply take the printer-ready data stream and get it into the printer (and provide the extra functions if it is supposed to do so. In some cases this is done by an additional daemon, which listens to requests to access the different features of the printer.

The low-level communication module and the filter should be separate pieces of software, to integrate well with the printing system and also to work if a client sends pre-rendered data (for example a Windows box with the Windows driver).

Printer maintenance and auxiliary tools: Programs to do tasks like observing the ink/toner levels, cleaning and/or aligning print heads, handling incoming and outgoing faxes and fax numbers, … These tools are called like application programs and can make use of a printer-specific low-level communication module. There should be the possibility to do the tasks with a graphical user interface for best usability on desktop machines but also an option to do things via the command line, so that the printer is also accessible from scripts or by blind people with a Braille text interface.

Scanner drivers: Scanner drivers for multi-function devices should not come in the form of desktop applications, they should come as drivers for SANE, so that every scanning software for Linux can be used with the built-in scanner. This also saves the cost and effort of writing a scanning GUI.

Available infrastructure and integration

On the current Linux distributions printing is done via the Common Unix Printing System (CUPS). It takes care of making printers available on the local machine and in the network, control printer access, collect and route print jobs, and finally filter and print them.

CUPS: In a CUPS environment the filters and low-level communication modules are all running on the server side (the machine where the printer is connected to). On clients (remote machines to which the printers are shared) one does not even need to set up print queues. as CUPS supports auto-discovery of servers.

CUPS supports several different kinds of auxiliary programs, especially Filters to convert one file format into another and backends for low-level communication with printers. So printer drivers integrate in CUPS as CUPS filters for rendering PDF to the printer's language and CUPS backends to do the low-level communication. CUPS ships backends for standard interfaces and communication protocols, so a printer driver usually does not need to ship a CUPS backend.

As under Linux and Unix all applications produce PDF (formerly PostScript) when they send data to the printing system CUPS print queues are emulating PDF printers (CUPS understands also other input formats, like PostScript, images, or plain text, but it usually converts these formats to PDF, note that some of the extra input formats will get deprecated soon). The capabilities of PostScript printers and their user-settable printer-specific options are described in PPD (PostScript Printer Description) files supplied by the printer's manufacturer. For non-PostScript printers CUPS also uses PPD files, but with special CUPS-specific information added, especially which CUPS filter generates the printer's language. PPD files for non-PostScript printers are usually shipped with the driver. The printing options to control the printer and the driver (paper trays, paper size, output quality, color adjustment, …) users see in printing dialogs of applications or in printer setup tools are the ones described in the PPD file.

Ghostscript: To render PDF (and also PostScript) Ghostscript is available on all Linux and Unix operating systems (see above). Ghostscript is called by one of the CUPS filters. In case of CUPS raster drivers this is done by a filter named “pdftoraster” which has the CUPS raster format as output and a CUPS filter shipped with the driver package converts this into the printer's language. For the other drivers there is usually one CUPS filter which converts PDF directly to the printer's language. This filter calls either Ghostscript with one of the built-in drivers or with a plug-in (IJS or OpenPrinting Vector), or it lets Ghostscript produce a standard image format (PNM, PNG, TIFF…) and pipes it through a filter which generates the printer's language.

foomatic-rip: foomatic-rip is a universal print filter. It can be used with all printing systems (including CUPS) auto-detecting the system by analyzing the environment out of which it got called and can call printer drivers (renderer/filter) with arbitrary command lines which have PDF as standard input and the printer's language as standard output. foomatic-rip uses PPD files (with extra keywords for non-PostScript printers and advanced option types) to know about the driver command line and the user-settable options. This way it also adds PPD file support to non-CUPS printing systems. It has also some extra features like page overrides (different option settings for selected pages), making CUPS raster drivers working with non-CUPS printing systems, and converting non-PDF input data on non-CUPS printing systems.

foomatic-rip is available in all Linux distributions (used as a CUPS filter) and in Sun's Solaris (used with the LP printing system) and is the de-facto standard for using most printer drivers.

CUPS DDK: This does not ship with the Linux distributions which shipped CUPS 1.3.x and older (from CUPS 1.4.x on CUPS DDK is an integrated part of CUPS), but it is free software which you can download from the CUPS web site and provides interesting tools for building and manipulating PPD files and also two CUPS raster drivers which can be used as a base for your own driver development or as an example for driver programming. As the CUPS DDK ships very convenient tools for generating PPD files it makes part of the LSB DDK. Note that the LSB does not require the presence of the drivers which come with the CUPS DDK. So do not make driver packages based on the drivers coming with the CUPS DDK (or ship these drivers with your package).

SANE: Every Linux distribution ships SANE to access scanners and all free scanning software under Linux uses SANE. So if you want to provide full functionality for a multi-function printer with scanner, base your scanner driver on SANE. This way every user can use the scanning software he is used to, and easily switch between different scanners without needing to learn to use a new interface all the time. SANE is also included in the LSB DDK now, but this part is not yet intensively tested and SANE is also not yet in the LSB. SANE modules compiled in the LSB Build Environment with the LSB DDK should work on all distributions with SANE 1.0.x. It is planned to make SANE a requirement for LSB 4.1.

The Linux Standards Base (LSB): To assure common binary interfaces and directory structures across all major Linux distributions and to make it this way possible to create distribution-independent binary Linux application packages, the LSB was created. The LSB defines standards for common interfaces, system functiuonality, and directory structures which are found in all currecnt major Linux distributions. The current version of the LSB, version 3.2, has requirements for a modern printing infrastructure for the first time. Current [OpenPrinting/Database/DriverPackages distribution-independent driver packages] in the OpenPrinting database are based on LSB 3.2 and so they require nothing else then an LSB-3.2-compliant system, which is the case for most of the current Linux distributions.

Note that LSB 4.0 did not add any new requirements for printing.

There are also older LSB-3.1-based packages, but LSB 3.1 did not yet have any printing-related requirements. So that driver packages require CUPS, Ghostscript (ESP 8.15.3 or later, GPL 8.60 or later), Perl, and foomatic-rip to be installed in addition. It must also be assured that CUPS searches for PPDs in /usr/share/ppd then. See the instructions for installing the driver packages.

In the LSB 3.2 the following requirements were added:

  1. The most important interfaces of the CUPS convenience API must be available. See the LSB page about CUPS
  2. Printing interfaces of Qt 4.0
  3. The driver interfaces CUPS Raster, IJS, and OpenPrinting Vector
  4. Ghostscript with at least the output devices “cups”, “ijs”, “opvp”, “pxlmono”, and “pxlcolor”
  5. foomatic-rip
  6. PPDs are searched in /usr/share/ppd

SANE was not added, it is planned to be added in LSB 4.1. LSB 4.1 will also require the full CUPS API, except interfaces which were dropped after CUPS 1.1.x.

Independent of that Perl and Python are also required by the LSB 3.2.

The OpenPrinting web site: The OpenPrinting database makes it easy for users and also for printer setup tools to find the best driver for a given printer. Now it does not only provide information about printer drivers and PPD files, but it support also providing the drivers themselves. These drivers are made available as distribution-independent printer driver packages and a web API is provided so that printer setup tools can look up printer drivers, present information about the driver to the user and download and install the driver automatically when the user agrees. As this can be done automatically by a printer setup tool, you do not need to get your driver into all the distributions if you provide it in the OpenPrinting database. Once your driver is there, your printer will “just work” for all Linux users with internet connection.

Other resources: The LSB requirements are only a worst-case scenario of features available in currently supported Linux distributions. As they are defined by what distributions already contain and especially cover the enterprise-level long-term-support distributions from Red Hat and SUSE which can already be several years old, many resources which are common in the current desktop distributions are not covered. Examples are D-Bus or PolicyKit.

It is still possible to make use of them in LSB-based packages. What has to be done is that the appropriate libraries have to get statically linked into the executables or shipped as part of the package and taken care that the package's binaries (and only them) use the library shipped with the package. If the resource is a service, like D-Bus, the driver should determine its availability at run-time and use it if it is available, but not fail if it is not there.

What to do and what not to do


Many printer drivers, especially drivers supplied by printer manufacturers, have problems due to a bad design which makes the drivers difficult or complicated to install or even break things in the user's system including security problems. This often happens when driver developers are used to Microsoft Windows where the architecture of the operating system and the printing infrastructure is well different.

A big difference in the printing infrastructure is that under Windows printer drivers are running on the client machine (the machine which runs the desktop applications). The driver is usually called directly by the application and so it can communicate with the application bi-directionally, can add GUI elements to the application's print dialog and can also pop up its own windows. The application then sends the data in the printer's native language to the spooler. This means also that every client machine which wants to print needs the driver locally installed.

Under Linux the printer drivers are running on the server (the machine to which the printer is connected). Applications send the data in a device-independent page description language (PDL), usually in PDF. The driver on the server converts this device-independent PDL into the printer's native PDL. The advantage is that the driver is only installed once on the server and does not need to be downloaded by the clients. Disadvantage is that the driver cannot be interactive and needs to be remote-controlled somehow by the desktop application on the client. This is done via PPD (PostScript printer description) files. The PPDs describe the properties of the printer/driver combo and which parameters can be set by the user. According to this the application adds appropriate GUI elements to its printing dialog. The types of parameters and GUI elements are restricted, as PPDs do not support arbitrary data types for the user-settable parameters.

Also important to say is that Linux is a multi-user operating system where every user has only access to his work data and where there is an administrator (“root”) who has access to the whole system. For security reasons also some system processes (like the conversion of print data to the printer's language) are run as non-root users.

There are also some stability and security policies under Linux which do not necessarily exist under Windows, for example that a package cannot overwrite a file from another package, that user-callable desktop applications should run with the rights of the calling user and not as root or as another user, …

For printer/scanner drivers it is also important that they should be installable in any combination, packages for two different models should not have a file in common, as there can always be users having these two devices connected to one computer. And distributions want to be able to have all printer drivers installed at once, so that a user can connect an arbitrary printer and it just works.

So here are some guidelines of what to do and what not:

  • Setup of print queue must work with distribution's printer setup tool: Let you driver not depend on a special printer setup tool which you ship with the driver. Users are used to work with the printer setup tool which comes with their distributions. They do not expect that the driver package installs another tool and they do not know where they will find the new tool. The distribution's setup tools often do their work already automatically when simply plugging a printer to the USB. Also an automatic download and installation of the driver package by the printer setup tool will not work if the It is also confusing if you have more than one printer and for each printer you need another printer setup tool.
  • Also printing itself must work with the system's software: Also printing must work with the system's software and should not require any special software coming with the driver, like for example a special printing dialog replacing the “lpr” command. Once, many applications will not use such a program, as nowadays most of them do not call “lpr” any more. They talk to the CUPS spooler directly via IPP. Also users do usually not call “lpr” directly as one can easily print from every application. In addition, replacing “lpr” by a GUI application will break scripts which print completely non-interactively.
  • Provide one PPD file for each supported printer model with distinct IEEE-1284 device ID: For automatic print queue setup the distribution's printer setup tool reads the printer's IEEE-1284 device ID and finds a matching PPD file for it. This only works if there is a separate PPD file for each device ID. If there is for example a PPD file like “EasyPrint FooJet 2000/3000/4000” the tools will have problems assigning it to a detected EasyPrint FooJet 3000.
  • The physical PPD files must be present after installation of the driver package: When the driver package (RPM, Debian) is installed, the PPDs for all printer models supported by the driver must be present, accesible by recursively searching /usr/share/ppd, /usr/local/share/ppd, and /opt/share/ppd (usually a symbolic link from the physical PPD location is pointing into one of these directories). It should not be required to manually call a program to generate the PPDs. The PPDs should be generated by the package build process or in the post-install script at the latest. The on-the-fly PPD generators of CUPS 1.2.x (in /usr/lib/cups/driver) are not supported by the LSB 3.2 as this version of the LSB still supports CUPS 1.1.x. They are under consideration for LSB 4.1.
  • Do not require PostScript as input data format for your driver: In modern Linux distributions applications emit PDF data streams when one sends a document to the printer and CUPS uses filters to process the PDF directly. PostScript as print job format is deprecated. For drivers which send bitmap (raster data) as output use CUPS Raster as input, if your CUPS filter is a wrapper around Ghostscript, write it in a way that both PDF and PostScript are accepted as input or at lease PDF.
  • Do not require from the user that he has to run command line tools: Users get the driver as a RPM or Debian package and after installing the package it should be enough to set up the print queue with the distribution's printer setup tool. Not every user reads the README files to find out what he has to do and there is also the possibility of an automatic setup where the printer setup tool installs the package and sets up the print queue. Note that you can run fully non-interactive command line programs also from the maintainer scripts of a package (pre/post install/uninstall scripts).
  • Do not require the printer to be connected and turned on while installing the driver package: The installation of the driver RPM or Debian package should be independent of the presence of the device. The driver package could be installed as part of the Linux distribution and only some time after the system installation the printer gets connected.
  • Do not modify the system's configuration and do not modify or replace the system's files: A package should not overwrite system files with its own files, as these files for sure serve also for other things than only this printer driver. For example a system library should not be replaced by a patched version coming with the driver, otherwise other programs using this library can misbehave or even stop working completely. Such changes will also get lost if the distribution ships a security update for this library. Also replacing of system utilities like “lpr” by a printing GUI can break things, as for example non-interactive scripts which print something. Even changing only the permissions and/or ownerships of a file can break things severely. For example a manufacturer-supplied printer driver package has made SUID root which allowed normal users to overwrite system files and prevented users to use their documents with other programs than
  • Avoid the need of specialized kernel modules: Printer drivers by themselves are filters which turn PDF into the printer's native language, a task which can always be done in user space. Device access usually is done with standard methods for which appropriate kernel modules already come with the operating system. If the usblp kernel module does not allow to access a certain printer with all needed functionality, use libusb to access with more liberty. Note that a crashing kernel module can crash the whole system and that you cannot make a distribution-independent LSB package if your driver ships a kernel module.
  • Avoid world-writable files: World-writable files are a big security hole, as any user can modify them, independent of the owner of the file. Especially system files in /usr/… and /etc/… cannot be accepted as world-writable files.
  • Allow flexibility in where to install the files: Do not hard-code directory paths in closed-source binary files. Put them into easy-to-modify configuration files. This way your package can be built with alternative installation directories, as packages coming with the distributions usually install into /usr and non-distribution packages like the distribution-independent driver packages here at OpenPrinting install into /opt/<supplier>.
  • Do not allow normal users to change system-wide defaults: If a user changes option settings and saves them, these changes should only be valid for this user and not for all users. Otherwise other users will get unexpected results when printing. Especially when various users print different documents on the same printer. If a user changes options while other user's jobs are queued, that jobs would come out with changed settings, too. System-wide changes should only be allowed to root and to authorized users (like of the “lpadmin” group in the case of CUPS).
  • Avoid file conflicts between your driver packages: Make sure that there are no two driver packages in your collection which have files with equal names and paths. This way the user cannot install the two packages and so he cannot run the two printers which they support on one and the same computer. It must be possible to install all driver packages which you provide at once, as the user can have any arbitrary combination of your range of printers.
  • Do not create too many too small packages: If you support 100 printer models with your drivers, do not create 100 packages. This makes it impossible for Linux distributions to ship your packages and it is also much more difficult to maintain these packages (security fixes, …). Your printers will not use 100 completely different page description languages. A typical printer manufacturer with 100 printers has perhaps 10 completely different wire protocols to get the data into the printer. This means you can make 10 packages and each of them supports all printers with the appropriate protocol. This makes it also easier for you to provide LSB packages.
  • Make sure that your printer is uniquely identified by its device URI: For network printers with standard CUPS backends there is no problem, as they have unique IP addresses. For USB printers with the standard CUPS USB backend you are on the safe side as long as you do not connect two printers of the same model to one computer, as CUPS makes URIs from the manufacturer and model name. For two printers of the same model CUPS distinguishes by the serial number in the IEEE-1284 device ID. So talk with the hardware engineers that they expose the serial number in the device ID (HP does it for example). If you cannot use standard CUPS backends and write your own backend, make sure to never let it address printers by /dev/usb/lp0 or similar device files. If you have two or more printers and reboot, they can get assigned to the printers in different order. Also be very careful with the USB vendor and product IDs. There exist completely different printers with the same vendor and product IDs, like most Epsons. The best for identification of locally connected printers is always the device ID, and the serial number for printers of the same model.

Making distribution-independent printer driver packages using the LSB DDK

The most user-friendly way to supply printer drivers to end-users are binary packages which they can simply install with the tools which come with their distributions. For printer manufacturers sometimes it also the only way to publish drivers, as they contain IP which they cannot publish as open source. Unfortunately, the usual binary packages as shipped with Linux distributions work only with one specific distribution. Also binary packages of drivers shipped by printer manufacturers or posted by free driver projects are usually only for specific distributions. For manufacturers this means duplicate packaging and testing effort to support their printers under Linux, or they restrict their support to only one or two distributions.

To reduce costs and development time the LSB Driver Development Kit (DDK) was developed. The LSB itself was designed to allow the creation of distribution-independent packages of application programs. The LSB DDK expands the LSB development environment to support building and packaging printer and scanner drivers. It consists of the printing environment (LSB 3.2 requires only certain interfaces and therefore the standard development environment does not contain the complete printing environment) and of a macro set for RPM for easy packaging. This way you can create distribution-independent printer driver packages with the not much more effort than distribution-specific ones. So you package and test once for all distributions. Then printers will work with Linux and not with Red Hat, SuSE, Debian, …

Installing the LSB DDK


At first you need to install the LSB Build Environment as described in the LSB developer tutorial. If you install RPM packages inside the chroot environment (“lsb-buildenv”, not yet available for LSB 3.2) you need to supply always the “–nodeps” option to the “rpm” commands, as the chroot environment is not completely built with RPM like a real Linux distribution, and therefore the RPM database is not complete. Your build environment should contain the packages (click the package names for download locations) “lsb-build-base”, “lsb-build-cc”, “lsb-build-c++”, “lsb-build-libbat”, “lsb-appchk”, “lsb-pkgchk”. lsb-build-libbat is not required, but can make the life eaiser. Check the presence of the packages with “rpm -qa” and install the missing ones with “rpm -Uvh –nodeps <package file name>”. On Debian-based distributions use “dpkg -l | grep lsb” to see what is already installed and install the Debian packages with “dpkg -i <package file name>” (add “–force-depends” if needed). If there are some packages only available in RPM format use “alien -ick <package file name>” to install them (this converts the packages to equally-named and -versioned Debian packages before installing). See also the tutorial. Install also your distribution's “lsb” and “lsb-printing” packages (if available).

If your driver comes with auxiliary programs with graphical user interface you will also have to install the other “lsb-build-...” packages for X and the GUI.

For easier installation and automatic updating of the LSB packages from the Linux Foundation, add the indexed package repository to the package download sources list of your distribution's package manager. The needed information you will find on our Repository Info page.

Every library which is not provided with the LSB Build Environment must be statically linked by the executables in your driver package. The same you have to do with every library from which you are using interfaces which are not covered by the LSB. Alternatively, you can ship the appropriate shared library with your driver but then you have to make sure that only your driver uses your copy of the library and no other program on the system.

Now you need to install the packages of the LSB DDK onto your machine where you have installed the LSB Build environment. Currently, this is the RPM macro set, the CUPS DDK (RPM packages “cupsddk...”), and the sane-backends package (RPM packages “sane-backends...”). The latter is only needed for scanner drivers (also to support the scanning function of multi-function printers). Append the RPM macro set to the file “/etc/rpmrc” or “.rpmmacros” in your home directory. If the file does not exist, create it. Add also the following lines (with the correct data filled in) to “.rpmmacros” in your home directory:

%_topdir                /home/YOURLOGIN/rpm
%_tmppath               /home/YOURLOGIN/tmp
%distribution           LSB
%vendor                 YOURORGANIZATION
%packager               YOURNAME <YOURMAILADDRESS>

Now everything is prepared to build printer driver packages.

It is highly recommended to build the driver packages in a chroot LSB Build Environment, it is much more safe to get LSB-compliant executables there. Currently, as there is no LSB Build Environment chroot for LSB 3.2 yet, build on a usual system. Use lsbappchk on all executables and shared libraries of you package, to make sure that you really get LSB binaries.

Compiling and packaging the driver to be LSB compliant

All distribution-independent LSB packages are provided as RPM packages. For non-RPM distributions (like Debian or Ubuntu) the LSB requires alien to be in the distribution. This program converts RPMs to other package formats. So what you need to know at first is how to make RPM packages. This will not be covered by this document, as there is good documentation elsewhere.

Our RPMs will differ from packages shipped by Linux distributions in the following points:

  • All files belonging to the package install into /opt/<supplier>/. This way packages do not conflict with parts of the distribution or other LSB packages. Each supplier has to take care that in his /opt/<supplier>/ nothing conflicts for the case he publishes more than one package. This is a requirement of the FHS (File system Hierarchy Standard).
  • To make the system finding PPD files, executables, CUPS backends, … the maintainer scripts (pre/post install, pre/post uninstall) set symbolic links, add lines to configuration files, … Note that the locations of the system's directories to link into and the system's config files differ from distribution to distribution, so this cannot be hard-wired into the package. The maintainer scripts (scripts which come with the package and are executed during package installation and removal: pre/post-install and pre/post-uninstall) are the only solution, as they can act depending on the system on which the package will be installed.
  • The maintainer scripts are designed in a way that they work in both RPM and Debian packages (Debian packages generated with “alien –scripts <RPM file name>”).
  • The maintainer scripts have to work with all major distributions.
  • PPD files are linked to /usr/share/ppd/<supplier>/ and inside this directory arranged in subdirectories for each printer manufacturer and named <Manufacturer>-<Model>-<driver>-<language>.ppd. The language is the two-letter abbreviation (ex.: de) or the combination of a two-letter abbreviation with the uppercase two-letter abbreviation of the country, connected by an underscore (ex.: pt_BR), as it is also used by the Linux operating system.
  • The packages must contain at least PPD files. Other files are only required if they are needed to be able to print with the PPD files (like driver executables). PPD files are needed for the printing system to associate the driver with the supported printer models. A package can consist of only PPDs if it is for PostScript printers or for standard built-in Ghostscript drivers.
  • The post-install script of the packages has to update the PPD files of already existing print queues (usually in /etc/cups/ppd/) which got set up with previous versions of this package. It should not replace PPD files of supported printers which use drivers from other packages.

These things are usually very complicated to do with stock RPM. To make this task as easy as possible there is the RPM macro set which will do most of the stuff by adding only a few simple lines.

Before you start creating your own RPMs you should have a look at the spec files of existing distribution-independent driver packages. If your driver has more complex requirements as needing to install and run a daemon or being split into several binary packages, have a look at the CUPS and Ghostscript packages.

If you have already made usual RPMs of your driver, you can take their spec files and easily convert them to LSB packages by adding the appropriate macros of the macro set. Otherwise create a new spec file and add the macros.

Now we go through the sections of the spec file to show the changes which have to be done to turn the package into a distribution-independent LSB package. The macros are presented in the order how they are usually applied in the spec file.

RPM Header

This is the part before the %prep section begins. Here the following has to be present:

Name:		splix
Version:	1.0.1
Release:	3lsb3.2

The package name should be the same as commonly used in distributions (and would be great to have it also being the driver name used in the OpenPrinting database). Please only use letters, numbers, and dashes for the package name. RPM also allows underscores, but your package will also be converted into a Debian package and the Debian package format does not allow underscores in package names.

The version number is the version number of your printer driver or if you package someone else's driver the version number of the upstream (source) package. The release number has “lsb” and the version number of the LSB which we are building for as its suffix (our “distribution” is LSB).

License:	GPL

This is the license of the driver which you are packaging. For closed-source drivers use “Commercial”.

Group:		Applications/System

Always use this group for drivers.


URL where you present the driver on your web site.

%define supplier splix

For free software projects the name of the project, for companies (like printer manufacturers) the company name. This is used as a file name, so use only characters allowed in file names and avoid spaces. This one is used (and required) by the macro set.

%define supplierstr SpliX

A more human-readable supplier name. Is required by the %adjust_ppds and
%update_ppds_fast macros. As its value appears in the NickName of the PPD files it can contain all characters which are allowed in the NickName. Only “:” is not allowed.

%define drivername splix

The name of the driver, in most cases identical to the name of the filter executable or IJS plug-in. Should be the same as the name of the driver database entry in the OpenPrinting database. Also this name is used as a file name. And do not create driver names with spaces for the OpenPrinting database. Also used and required by the macro set.

%define driverstr SpliX

A more human-readable name of the driver. Is required by the %adjust_ppds and %update_ppds_fast macros. As its value appears in the NickName of the PPD files it can contain all characters which are allowed in the NickName. Only “:” is not allowed.

%define ppdnickname - %{driverstr} %{version}%{extraversion} (%{supplierstr} LSB %{lsbver})

This macro is already predefined as in this example line. Therefore it is optional and only needed if you want to define it differently. The %adjust_ppds macro requires this and replaces the NickNames of the PPDs by manufacturer and model (separated by a space) plus this string. It is highly recommended to not supply this macro, so that the definition shown above gets used. Then all relevant info is coming from other macros. The %update_ppds_fast macro requires that the PPD's NickName contains the value of %driverstr and afterwards the value of %supplierstr, both as separate words, so take this into account when defining your own version of this macro.

%define filternames <driver executable name,...>

This lists the names of the driver executables for which the %adjust_ppds macro (%install section) has to add absolute paths. There can be more than one driver executable name be supplied in a comma-separated list. If the driver executable name is given with a full path, this path will be used in the PPD file, otherwise %_bindir. Do not use this macro with CUPS raster drivers. Here %adjust_ppds finds the CUPS filter executables by itself.

%define services <service name,...>

This lists the names of the system services supplied by this package. There can be more than one system service be supplied in a comma-separated list. These services will be set up for being started at boot by the %setup_services macro (in the %post post-install script), restarted by the %restart_services macro (in the %post post-install and the %postun post-uninstall scripts when the package gets updated), and removed by the %remove_services macro (in the %postun post-uninstall script).

BuildRequires:  lsb-build-cc, lsb-build-c++, lsb-appchk
BuildRequires:  perl, gzip

If you use the lsb-build-libbat package, add

BuildRequires:  lsb-build-libbat

These are the packages needed to build packages based on the LSB DDK.

Requires:       lsb >= 3.2

Do require always the “lsb” package of at least the version for which you build the package. Be careful with other Requires, they need to be available on all LSB-compliant distributions (at least commonly available, not necessarily required by the LSB) and always with the name which you specify. You do not need to require any package which the LSB requires. These packages are already required by the “lsb” package.

BuildRoot:	%_tmppath/%name-%version-%release-root

Do not forget to supply a BuildRoot, otherwise the package will mess up with your system (to make this not happen accidentally do not build packages as root).

BuildArch: noarch

If your package does not contain any binary executables, either because it consists of only PPD files or all driver executables and auxiliary programs are scripts, add “BuildArch: noarch” to the RPM header. Otherwise, do not do it.


This is the first macro of the macro set which we use. It makes the packaged driver getting installed into /opt/<supplier>/, by redefining all standard directory macros of RPM, like %_prefix, %_bindir, %_datadir, … It also defines the boolean macro %optinstall, so that you can easily make your package installing in /opt/<supplier>/ or at the usual location, simply by commenting out the %install_into_opt call and using “%if %optinstall” expressions.

This macro also defines the macros %_cupsserverroot, %_cupsserverbin, %_cupsdatadir, and %_cupsppd pointing to locations in /opt/<supplier>/ for CUPS config files and file auto-detection/conversion rules (/opt/<supplier>/cups/etc/), CUPS filters/backends (/opt/<supplier>/cups/lib/), for CUPS-related data files (/opt/<supplier>/cups/data/), and PPD files (/opt/<supplier>/ppds/). These directories (or the files in them) are automatically symlinked to the appropriate locations in the destination system by the maintainer scripts. So CUPS-related files should not be installed to the places which “./configure” detects from the CUPS installed on the build system, but into the locations where these macros point at, for example with appropriate command line options at the “make” command or by executing the macro %adjust_cups_dirs right after running “./configure”, which replaces all occurences of the auto-detected CUPS paths anywhere in the source tree by the paths in /opt/<supplier>/.


This three macros serve for appropriate search paths being set on package installation. Call the first if your driver contains auxiliary programs to be executed by normal users. This adds %_bindir (/opt/<supplier>/bin/) to the $PATH. The second is for drivers with auxiliary programs to be used only by the administrator as it adds %_sbindir (/opt/<supplier>/sbin/) to the $PATH. The third you should use if you ship man pages. It creates a $MANPATH and adds $_mandir (/opt/<supplier>/sbin/) to it. In addition, these macros set %binexec, %sbinexec, and %manpages to 1. These macros are ignored when %optinstall is not set (no call of %install_into_opt), as they do not make sense when the files get installed into the usual directories of the system.

%prep section

After executing the commands in this section there will be automatically generated a file <source package name>.sh which will be installed into /opt/<supplier>/profile.d/ after the commands in the %install section got executed. This shell script contains all the commands to do the needed extensions on the $PATH and $MANPATH as requested via the %has_bin_executables, %has_sbin_executables, and %has_man_pages macros. The maintainer script put the file into /etc/profile.d/ or append it to the end of the file /etc/profile, so that the changes are applied whenever a login shell is opened. If not %install_into_opt and at least one of %has_bin_executables, %has_sbin_executables, and %has_man_pages were called before, no file is created.

%build section

Before executing the commands in the build section, the following environment variables are set:


This way it is taken care that the LSB compilers and LSB-provided shared libraries are used.


Using the %configure macro (a macro already available in stock RPM) instead of directly using the “./configure” command sets automatically all installation directories. This is especially important in our packages where everything gets installed into subdirectories of /opt/<supplier>/ (using the %install_into_opt macro in the RPM header). The macros for the CUPS file locations %_cupsserverroot, %_cupsserverbin, %_cupsdatadir, and %_cupsppd do not get applied by this macro, as there are no standardized “configure” command line options for the CUPS directories. Most “configure” scripts do not allow setting these directories at all, they always use the directories auto-detected from the build system's CUPS installation.


As “configure” scripts usually set the directories for the CUPS-related files to the ones auto-detected from the CUPS installation on the build system, the directory settings need to be manually changed to %_cupsserverroot, %_cupsserverbin, %_cupsdatadir, and %_cupsppd after running the “configure” script. In some cases it can be done by supplying command line options to “make” in other cases files need to be patched. The %adjust_cups_dirs does the patching automatically. It searches all files in the source tree for occurrences of the auto-detected CUPS directories and replaces them with the directories defined by the macros %_cupsserverroot, %_cupsserverbin, %_cupsdatadir, and %_cupsppd. The call of this macro is ignored when the %install_into_opt macro was not called.


This macro sets all important installation directories on the command line of “make”. This is needed if there is no “configure” script or if “configure” does not set all directories in the Makefiles. Stock RPM does not provide this macro.


Many driver packages ship Foomatic XML data from which the PPDs are generated. To generate the PPDs the Foomatic packages “foomatic-db” (the Foomatic XML database) and “foomatic-db-engine” (the software to access the database and to generate the PPDs) are needed. After unpacking these two packages into the base directory of the source tree and adding the Foomatic XML files of this package to the unpacked “foomatic-db” via the %prep section and setting the %drivername macro correctly, a call of %build_foomatic_ppds generates the PPDs automatically. They will be available in the ppd subdirectory of the source tree. Take care to install them into the %_cupsppd directory in the %install section.

%install section

The environment variables CFLAGS, CXXFLAGS, and LDFLAGS are set the same way as in the %build section, before the commands in the %install section get executed. After the execution of the commands in this section the file <source package name>.sh gets installed into /opt/<supplier>/profile.d/.


This macro has the same motivation as %configure and %make. It sets all important system directories using the RPM macros via the command line. It also preceeds every directory setting on the command line with the %buildroot macro. So the software gets installed into the temporary build directory set by “BuildRoot:” in the RPM header and not into the system. This is very convenient if a Makefile does not provide a DESTDIR option. This is a macro of stock RPM.


The printer driver packages usually provide simply the software to make a certain class of printers and multi-function devices work. They do not supply libraries which can be used by other programs. So they do not need to ship C headers, static libraries, or pkgconfig files. Calling this macro after “make install” removes all these files and the %_includedir.


This macro, called after “make install”, rearranges and renames the PPD in %_cupsppd to conform with the agreements for LSB 3.2. All PPDs are put into subdirectories named by the printer manufacturers and renamed to <Manufacturer>-<Model>-<driver>-<language>.ppd. The manufacturer and model names and the language is taken from the PPD file and the drivername from the %drivername macro.

It also adds absolute paths to the filters in the “*cupsFilter” lines (except foomatic-rip, as foomatic-rip is part of the system and should not be shipped with the driver) and in the “*FoomaticRIPCommandLine” lines. For CUPS filters the path %{_cupsserverbin}/filter/ is added. For calls of executables coming with the driver being recognized as such the %filternames macro has to be set before calling %adjust_ppds. It must contain a comma-separated list of all executable names. Then %_bindir is added as absolute path. If a name with absolute path is given in %filternames, this path will be added.

With the macro %set_ppd_links being executed in the post-install script the PPDs get accessible as /usr/share/ppd/<supplier>/<Manufacturer>/<Manufacturer>-<Model>-<driver>-<language>.ppd, as agreed on for the LSB 3.2.

In addition, the NickNames of the PPDs are modified to contain standardized information about manufacturer, model, driver name and version, supplier, and LSB version. You can take influence on this by the %driverstr, %supplierstr, and %ppdnickname macros. This is especially needed for the %update_ppds_fast macro to work.


There are special tools to set up and remove the distribution-specific links for the init scripts of services to be started on boot (links in /etc/rcN.d or /etc/init.d/rcN.d). So these links are set by the post-install andremoved by the pre-uninstall scripts using the LSB tools. Calling this macro removes such links created by “make install”.

All maintainer scripts


This macro executes /etc/profile and the scripts in /etc/profile.d. It should be called in the beginning of each maintainer script to set the paths so that programs installed into /opt can be accessed by the maintainer scripts.


Command which should only be executed during an update and not during a removal of the package should be bracketed by these two. This works both directly for the RPM package but also when converting the package to a Debian package with “alien –scripts”.

%pre pre-install script


This macro does nothing more than creating the three directories /opt, /etc/opt, and /var/opt. They usually have to exist on LSB-compliant systems but they do not exist always, for example in the LSB Build Environment chroot or in the LSB sample implementation they are missing.

%post post-install script


This macro either symlinks the file /opt/<supplier>/profile.d/<source package name>.sh (if it exists) into /etc/profile.d/ or appends its contents to the end of /etc/profile. This way it is assured that the paths for executables and man pages are set for the programs of this package being used when a login shell is started.

Ignored when package was built without %install_into_opt


This links all CUPS-related files into the system's CUPS directories: All files in %_cupsserverbin/filter are linked into the system's CUPS filter directory (usually /usr/lib/cups/filter), all files in %_cupsserverbin/backend into the backend directory (/usr/lib/cups/backend), and all *.types and *.convs files in %_cupsserverroot into the CUPS configuration directory (/etc/cups). This way CUPS finds the filters, backends, and file detection/conversion rules easily. This macro does not symlink the PPD files. Use %set_ppd_links for that.

Ignored when package was built without %install_into_opt


Symlinks the %_cupsppd directory to /usr/share/ppd/<supplier>, so that the PPDs are accessible as /usr/share/ppd/<supplier&gt/<Manufacturer>/<Manufacturer>-<Model>-<driver>-<language>.ppd to fulfill the agreements for the LSB 3.2.

Ignored when package was built without %install_into_opt


Updates PPD files of already existing local print queues which are using the driver supplied with this package. This assures that if the PPDs change in the new driver version (for example added or removed options or choices) that also already set up printers get the updated PPD files. Requires the %driverstr and %supplierstr macros to be set in the RPM header and the %adjust_ppds macro must have been called in the %install section after “make install” or %makeinstall.


Sets up system services coming with this package to be started at boot. For each service there must be a startup script in %{_rcdir}/init.d/ (/etc/init.d for conventional installation, /etc/opt/<package name>/init.d if package is built with %install_into_opt, then the scripts gets symlinked to /etc/init.d). If possible, the services are set up with the LSB tool /usr/lib/lsb/install_initd, if not common distribution-specific methods are used: “chkconfig<tt>” of Red Hat and derivatives, and “<tt>chkconfig” for Debian and derivatives. The names of the services (the init scripts) have to be supplied in a comma-separated list in the %services macro.


If this package is installing in /opt and has PAM configuration files in %_pamdir (/etc/opt/<package name&gt/pam.d), these files get linked into /etc/pam.d to give the system's PAM access to the files.


This does simply restart the CUPS daemon, so that changes like new *.types and *.convs files or in the case of CUPS 1.1.x being used new backends and PPDs, are taken into account by CUPS. The macro works with both “cups” and “cupsys” as service name for CUPS and uses Debian's “invoke-rc.d” if available.


With this macro all services of this package are restarted. The service names have to be supplied in a comma-separated list in the %services macro. Debian's “invoke-rc.d” is used if available.

%preun pre-uninstall script


These two macros bracket steps not to be done on an RPM update. This has to be done on things removing infrastructure, as on an update RPM runs the post-install of the new package before the uninstall scripts of the old package, which makes the stuff set up by the post-install removed again. If the package is converted to a Debian package with “alien –scripts”, the bracketed commands are executed, as uninstall scripts of the old version are run before the post-install scripts fo the new version.


These macros remove symlinks, files, and configuration settings which the corresponding %set… and %setup… macros have put into the system directories. So a clean removal of the package is assured.

%postun post-uninstall script


Same as already shown for the pre-install script. Bracketed commands are not executed on an upgrade of RPM packages.


Same as already shown for the post-install script. The first two take down CUPS-related links and the link to the PPD directory. The third restarts CUPS, the forth restarts all services as defined in the %services macro.

Which macros to use for which task?


  • %define supplier <supplier name> (RPM Header)
  • %define supplierstr <more human-readable supplier name> (RPM Header)
  • %define drivername <driver name> (RPM Header)
  • %define driverstr <more human-readable driver name> (RPM Header)
  • %install_into_opt (RPM Header)
  • %adjust_ppds (%install section)
  • %init_scriptlet (in the beginning of all maintainer scripts)
  • %create_opt_dirs (%pre pre-install script)
  • %set_ppd_links (%post post-install script)
  • %update_ppds_fast (%post post-install script)
  • %remove_ppd_links (%postun post-uninstall script, bracketed with %not_on_rpm_update … %end_not_on_rpm_update)

CUPS Raster driver

  • %adjust_cups_dirs (%build section)
  • %set_cups_links (%post post-install script)
  • %remove_cups_links (%postun post-uninstall script, bracketed with %not_on_rpm_update … %end_not_on_rpm_update)
  • %restart_cups (%post post-install and %postun post-uninstall scripts, in the latter bracketed with %not_on_rpm_update … %end_not_on_rpm_update)

Contains CUPS filters, backends, or *.types or *.convs files

  • %adjust_cups_dirs (%build section)
  • %set_cups_links (%post post-install script)
  • %remove_cups_links (%postun post-uninstall script, bracketed with %not_on_rpm_update … %end_not_on_rpm_update)
  • %restart_cups (%post post-install and %postun post-uninstall scripts, in the latter bracketed with %not_on_rpm_update … %end_not_on_rpm_update)

Contains auxiliary programs to be used by ordinary users

  • %has_bin_executables (RPM Header)
  • %set_opt_paths (%post post-install script)
  • %remove_opt_paths (%preun pre-uninstall script, bracketed with %not_on_rpm_update … %end_not_on_rpm_update)

Contains auxiliary programs to be used only by the administrator

  • %has_sbin_executables (RPM Header)
  • %set_opt_paths (%post post-install script)
  • %remove_opt_paths (%preun pre-uninstall script, bracketed with %not_on_rpm_update … %end_not_on_rpm_update)

Contains man pages

  • %has_man_pages (RPM Header)
  • %set_opt_paths (%post post-install script)
  • %remove_opt_paths (%preun pre-uninstall script, bracketed with %not_on_rpm_update … %end_not_on_rpm_update)

Has a “./configure” script

  • %configure (%build section)

Has a Makefile

  • %make (%build section)
  • %makeinstall (%install section)

Has Foomatic XML data and no ready-made PPDs

  • %build_foomatic_ppds (%build section)

Contains a shared library with C headers and pkgconfig files, but simply the driver should be provided, not development sources

  • %remove_devel_files (%install section)

Contains a daemon which should start on boot

  • %define services <service name,…> (RPM Header)
  • %setup_services (%post post-install script)
  • %remove_services (%preun pre-uninstall script, bracketed with %not_on_rpm_update … %end_not_on_rpm_update)

Contains a daemon which should be restarted on package updates

  • %define services <service name,…> (RPM Header)
  • %restart_services (%post post-install and %postun post-uninstall scripts, in both cases bracketed by %update_only … %end_update_only)

Contains a daemon and “make install” creates the (distribution-specific) links to the init script so that the daemon gets started on boot

  • %remove_startup_links (%install section)

Has PAM support and contains PAM configuration files

  • %setup_pam (%post post-install script)
  • %remove_pam (%preun pre-uninstall script, bracketed with %not_on_rpm_update … %end_not_on_rpm_update)


Before a package gets released it should naturally be tested. Here are the recommended testing steps:

LSB compliance of executables

To assure that the binary executables work on all distributions if only the “lsb” package is installed, the executables need to be tested for LSB compliance. To do so you simply call

lsbappchk <name of the executable with full path>

Do this with all binary executables, which you find either in your ~/rpm/BUILD/<name of source tarball>/ directory and its subdirectories or after installation of your package (see how to install the package in the “Does it really work?” section below) in


If there are complaints about interfaces being used which are not provided by the LSB, take care that libraries providing these symbols get either statically linked or shipped with your package.

Adobe-compliance of PPDs and use of PPD extensions

To assure that the PPD files work well together with the printing system and PPD-aware applications, check whether the PPDs are compliant with the Adobe specifications. To do this, run the command

cupstestppd <name of the PPD file with full path>

on every PPD file which you include in your package. If you get a “PASS” all is OK. In the case of a “FAIL” it is shown which specifications are not fulfilled.

Also test the use of the PPD extensions. For this download and install a test snapshot of the Common Printing Dialog and test the PPD with the dialog. Use both the Qt and GTK versions of the dialog. Especially check whether option and choice names appear translated, the options are presented with the right widgets, appear under the desired tags, icons show up correctly, …

Does it really work?

To test whether your driver package really works, install it in the LSB Build Environment chroot and also in real distributions.

Unfortunately, the chroot is not completely set up from RPMs, so most software which exists in the chroot does not exist in the RPM database of the chroot. So a simple RPM installation of your package will fail due to dependencies not being fulfilled. Therefore use always the “–nodeps” option when installing your packages into the chroot:

rpm -Uvh --nodeps <package file name>

To test actual printing out of the chroot, stop your system's CUPS daemon and then start the one inside the chroot. Or configure the CUPS daemon inside the chroot to use another port than 631 (“Port” or “Listen” directive in cupsd.conf, “ServerName localhost:<port>” in client.conf), to keep your system's printing environment working.

If access to the /proc file system is needed from within the chroot, either for observing the printing processes with “ps” or if your driver needs it, simply mount it manually using the command:

mount -t proc none /proc

For testing in a real distribution installation of the package should work as described in the user instructions on the OpenPrinting web site. Especially it should be enough to install the “lsb” package, CUPS, Ghostscript, Perl, and foomatic-filters (packages shipped with the distribution) to be able to install the driver package without the “–nodeps” option (or the “–force-depends” option for Debian).

After having installed your package, try to set up print queues with the printer setup tools which come with the distribution. Your driver should then appear as one of the possible choices, or it will be automatically selected by the tools if it is the only driver for the printer with which you are testing. If your package ships CUPS backends, check whether tey appear under the device connection types in the setup tool and whether they detect your printer correctly.

Try to print from different applications and with different option settings, to see whether everything works as it should work. Try also if your driver's auxiliary programs work correctly.

Check also whether the PPD files of already existing print queues (in /etc/cups/ppd/) get upgraded when you update the package and only queues which are using the driver from your package. If you “update” the installed package with itself (package of the same version) at least the file date of the PPD must get the current date. If you have done fixes in the PPDs, they must appear in the PPDs of the existing queues.


If you want to have some examples of driver packages, see these RPM spec files and these source RPMs. They were used for generating the appropriate binary packages in the OpenPrinting database.

Making your driver auto-downloadable

To make your new packages auto-downloadable via the OpenPrinting web site, you usually do not upload them to the OpenPrinting web server. You sign the packages in the way the packages coming with Linux distributions get signed and you set up an RPM and a Debian package repository on your server.

Then you link to these repositories in a driver XML file (a Foomatic XML file which will be added to the OpenPrinting database) which you create for your driver (later we will also give you the possibility to fill a web form instead of writing an XML file). The driver XML file will also hold information about the driver's license,

It is also highly recommended to supply printer XML files (for this there will be also a web form later) for each printer, as they can carry the exact IEEE-1284 device ID for matching with auto-detection results, a driver recommendation, and a lot of information useful for the user who browses the OpenPrinting web site.

Submit the XML files to us for review and import into the database and as soon as your entries are imported (or beginning from a release date you supply to us) users find your driver and printers on our web site or the driver gets even downloaded automatically when they plug in their printer.

Setting up the package repositories

Your files do not only need to get packaged but also put up on your download server as indexed package repositories, so that the package manager tools of the distributions (yum, apt, zypper, …) can easily download them and also automatically update them if you put up a newer version, for example a bug fix or security update.

The machine on which you generate the repositories should be an x86_64 or amd64 (64-bit) architecture with a 64-bit Linux installed. This is needed to convert RPM packages to Debian packages for both 32-bit and 64-bit RPMs.

Make sure that you have installed lsb, rpm (with rpmbuild), dpkg, fakeroot, alien, linux32 (util-linux), ia32-libs, apt-utils (apt-ftparchive), createrepo. The package names are for Ubuntu and they are most probably the same names for Debian. If you use Ubuntu, please activate the Universe repository as some of the packages are in Universe. On RPM-based distributions the names of the packages can be different.

It is no problem to generate both RPMs and Debian packages on the same machine, only installing packages is only possible using one of the two systems depending on your machine's distribution. But you do not need to install any of your packages on the server.

You start with RPM packages which you generate as described above. These packages must be arranged in directories similar as they are arranged when they are generated. In a base directory you create a directory named RPMS and there subdirectories named i486 for the 32-bit RPMs and x86_64 for the 64-bit RPMs. Architecture-independent packages, for example packages with only PPD files and perhaps also scripts (PostScript printers) go into noarch. Place the RPMs inside there and do not rename them. This looks more or less like this:

   `-- RPMS
       |-- i486
       |   `-- yourdriver-1.0.0-1lsb3.2.i486.rpm
       |-- x86_64
       |   `-- yourdriver-1.0.0-1lsb3.2.x86_64.rpm
       `-- noarch
           `-- yourpostscriptprinters-1.2.0-1lsb3.2.noarch.rpm

If you want to provide source RPMs as well, put them directly into an SRPMS subdirectory of your base directory, without subdirectories for architectures.

Very important: The directories which contain the packages (here RPMS/i486, RPMS/x86_64, and RPMS/noarch) must be browsable for the visitor of your site. Going to a URL like


should show the directory and not something like “Forbidden” or a redirection. This is important so that our site can find your packages with links containing wildcards.

Now your repository is ready for getting indexed. Go to your base directory and run the command

createrepo RPMS

If you have also source RPMs, run also

createrepo SRPMS

Now your RPM repository is indexed and so it can get added to yum or zypper source repository lists on client machines. Then the RPM packages can be easily installed with yum or zypper and if you update packages they get updated on clients by the daily automatic update mechanism.

For adding your driver repository to yum the user or the client printer setup tool has to create a file with the following content in the client's /etc/yum/repos.d/ directory:

name=yourcompany's LSB-based driver packages

For adding your driver repository to zypper the user or the printer setup tool on the client has to run the following command:

zypper addrepo -c -t rpm-md -n 'yourcompany' http://url-to-get-to-your-base-dir/RPMS/ yourcompany

You do not need to do anything for the clients to do this (we take care about that) but this data could be useful for you to test your archive.

Next step is converting the RPM packages into the Debian package format. This is rather complex as well as the indexing of the repositories, so we have prepared the mkrepodeb script (with component repository assignment rules components.lst) which does everything to import an RPM file into the Debian repository: Converting the RPM into Debian format including working around some shortcomings of alien, placing the Debian package correctly into the repository and finally re-indexing the repository.

Edit the “maindir” and “mainurl” definitions to use your base directory and the URL to access your base directory, then run the script with a binary RPM file as argument:

mkrepodeb yourdriver-1.0.0-1lsb3.2.x86_64.rpm

and you will get a file yourdriver_1.0.0-1lsb3.2_amd64.deb at the proper place in your Debian repository, outdated versions of this package will get removed, and the repository gets re-indexed.

Very important: The directories which contain the packages (here debian/dists/lsb3.2/main/binary-i386/ and debian/dists/lsb3.2/main/binary-amd64/) must be browsable for the visitor of your site. Going to a URL like


should show the directory and not something like “Forbidden” or a redirection. This is important so that our site can find your packages with links containing wildcards.

On a client machine the user or the printer setup tool has to add the line

deb http://url-to-get-to-your-base-dir/debian lsb3.2 main

to the /etc/apt/sources.list file and run

sudo apt-get update

Now your driver can be easily installed with apt-get.

Also here you do not need to do anything for the clients to do this (we take care about that) but also this data could be useful for you to test your archive.

Very important: Avoid as much as possible to move your package repositories to another URL. Once, the links in our database would not work any more, but we can quickly fix this if you inform us in time (or you can even fix this by yourself if we give you appropriate user rights). Second, users whose package managers are configured for using your package repositories as download source will get error messages instead of automatic updates.

Signing your packages

To make sure that the auto-downloaded driver packages are really yours they need to get signed. In general Linux distributions do not let packages get automatically downloaded if they are not signed and the key not trusted by them.

Generate a GPG key

See the GnuPG HOWTO for how to do this. Please also see some general guidelines about how to handle your GPG key to keep it secure.

Build a trusted path to distributions

This requires a physical identity verification by one of the following

  1. The Linux Foundation (LF) generates key and signs all the manufacturer's keys, the distributions sign LF's key
  2. The distributions directly sign the manufacturer's keys (needs physical meeting of a representative of each distribution with a representative of each manufacturer)
  3. Manufacturer uploads their key fingerprint to a web site with an SSL certificate which has been signed by an official registrar.In this case distributions cannot just download the key from the keyservers, because its validity cannot be verified automatically. However, a distribution developer can download the key, manually compare the fingerprint with the one mentioned on the web site, and include the GPG key into their distribution packages for driver setup. The distributions can not just sign the manufacturer's key there, since SSL-based fingerprint verification is much weaker than physical key signing. At least this is still much better than what the average user does when manually searching for and installing a driver.

The Jockey driver installer supports this mode of operation and provides building blocks for doing the SSL verification, GPG key download, verification, and installation. Since the location of the package GPG keyring and addition of third-party package repositories is distribution-dependent, distributors need to write some packaging-system-specific (apt, rpm, etc.) glue code to bind these together (See the change in the Ubuntu/apt implementation as an example). Using this facility, manual download, verification, and inclusion into distribution packages by the distribution developer is not needed any more.

Please see the GnuPG tutorial about key signing for details:

Get the GPG key into the keyserver network

See “Distributing keys” in the GnuGP manual.

Sign packages/repositories with the key

deb archives (Debian/Ubuntu/derivatives)

GPG signatures happen at two places:

  1. Source packages. The signature is contained in the “.dsc” file, which contains the parts of the source packages (tarball, diff), their checksums, and the GPG signature of this information. If you use the “debuild” tool to build your source packages, signing will happen automatically once your key is set up. Otherwise, you can sign a .dsc/_source.changes file pair with “debsign foo_source.changes”. Please see “man debsign” for details. If you use alien to generate .debs from RPMs instead of having real Debian source packages, this step is not needed.
  2. Archives. All .debs in an archive are listed in the “Packages.gz” file together with their checksums. In Ubuntu's archive you see all Packages.gz files and their checksums listed in the “Release” file of the archive. The Release file needs to be signed to provide a standalone signature. Run the command “gpg -abs -o Release.gpg Release” for doing so. This signature is then available in the Release.gpg file which can be verified for integrity with the manufacturer's public GPG key. See also the appropriate documentation at Debian.For an example, see (the Release and Release.gpg files). The package manager client software apt/apt-get does the signature check automatically.

The paths are examples for the Ubuntu archives. Generally they are composed as follows:

http(s)://<fully-qualified domain>/<path to archives>/dists/<distribution>/ 

The archives of OpenPrinting are located at

Note that these archives are not (yet) signed (no Release.gpg file)

RPM archives (RedHat/SUSE/derivatives)

RPM packages are signed with

rpm --addsign <package file> ... 

See further instructions via

man rpm 


Getting your driver listed by OpenPrinting

Now everything is ready so that you can make us listing your driver. As printer setup tools only look up OpenPrinting and not each individual manufacturer's site, this is essential for an auto-downloadable driver.

Currently you will have to create Foomatic XML files, one for the driver and one for each supported printer (printers are counted different models if they have different IEEE-1284 device IDs), later there will also be the possibility to enter data via web form. If you use scripts it is generally easier to create XML files.

The XML files get imported into our server's database and generate entries for each supported printer and for the driver. The entries are once visible as pages on our site and contain links to your packages for manual download and second, printer setup tools sending us the ID of a discovered printer which is supported by your driver get back links to your packages and package manager configuration data from us.

To create the XML files you proceed as described in the documentation of the Foomatic XML format, the README file of the foomatic-db package. Download the BZR repositories of foomatic-db and foomatic-db-nonfree and make also sure that your files do not replace an existing one. Especially if printer entries are already there for some of the printers which you driver supports, do not create a printer XML file, add your information to the existing file instead.

Very important is that your driver's XML file contains the license of the driver software, if the license is not a common free software license, the license text, support contacts (the distributions do not give support for third-party packages so there should be some way to contact the supplier of the driver), and links to your driver packages. Optionally you can also supply performance info about the driver and text comments. Also make sure to supply the URL search masks for the drivers to be found on your site and the links to the signature key fingerprint.

Printer XML files are optional but highly recommended as they allow you to supply the device IDs for exact identification of the printer model. You can also supply text comments about the printers and info about color, mechanism, and resolution.

Send us your XML files and we review them and also your packages. If all is OK we import the data from the XML files and your driver gets listed and auto-downloadable.

openprinting/writingandpackagingprinterdrivers.txt · Last modified: 2016/07/19 01:21 (external edit)