CAPS Universe documentation  1.0.4
All you need to know to be successful
The Idea behind libcapsppd

The Idea behind libcapsppd

PPD? What?

In the past the printer descripting files (aka PPD files, aka PostScript Printer Description files) were static files, used to describe the features a printer has and how to control them. This kind of description files is still in use, even if PostScript printers are less popular nowadays than in the past.

And with these static PPD files I always wonder, why I can select a DIN A5 paper format even if my printer has only one paper tray filled with DIN A4 paper. Or why I must select a label format from a list, even if the printer can detect the loaded label size by itself. That is why I wanted dynamic PPD files from the beginning: if my printer has only DIN A4 paper to print on, it should only provide DIN A4 format in the dialogues - and nothing else (makes no sense).
Or if a label printer detects a 6 cm by 3 cm label loaded, it should only provide this format to me in the dialogue. Everything else just confuses every user (including me).

That's why every printer driver must register itself with a descriptive data structure about its features: struct caps_ppd_base.

Here the description how to fill it correctly:

Printer Driver National Language Support

You should support NLS from the beginning. The idea is to not only print messages in the user's native language (which is the mostly use case I guess), also the PPD content should handled accordingly. Due to this, a user can read the printer's feature settings in PPD based printing dialogues in his/her own native language.

In order to support this, you should mark all translateable strings in your printer feature description with the NLS_() macro. Example:

.description = NLS_("Toner Save Mode"),
#define NLS_(string)
National Language Support related.
Definition: libcapsbase-local.h:61

The NLS_() macro expands to the plain string for the compiler. But the gettext tools will extract your sentenses and collect them into pot files for translation into different languages.

To make use of translated sentenses you must define your translation domain name to the libcapsppd via the caps_ppd_base::domain_name member.

Most of the time it is enough to just define this member to the local PACKAGE macro. This macro is defined by the autotools when the package's configure script runs. That's all. libcapsppd will make use of your package translations (if available in the requested language), when it creates the PPD on the fly.

Printer Driver Common Features

Printers shares many features. So they do not need a special description, a simple bit is enough to describe the feature.

Duplex/Single Tray/Manual Feed

The caps_ppd_base::common_features member describes such shared features.

  • LIBCAPS_GA_DUPLEX If you set this bit, the PPD contains all entries to make use of a duplex print. Don't set it, if your printer can only print in a simplex manner. In both cases the user sees the correct and possible options to print.
  • LIBCAPS_GA_SINGLE_TRAY If you set this bit, you define your printer has one paper tray only and it is loaded with one format of paper which cannot be changed (until you change the paper in the tray). In this case the PPD contains one paper format only and thus, the user cannot change it in the printing dialogue. If you don't set it, the PPD will contain all known paper formats and the user can select one from the list.
  • LIBCAPS_GA_MANUAL_FEED If you set this bit, you define your printer has a manual paper feed. In this case there is no fixed paper format like in a single tray, so the PPD will always contain all known paper formats which fits into the caps_ppd_base::media_coverage. And the PPD will contain a choice entry to let the user select the paper input source.
Note
Known paper formats are retrieved from libpaper. Thus, you can modify this list as well.
Only those known paper formats are included which are known to fit into the physical printing size defined in caps_ppd_base::media_coverage
The LIBCAPS_GA_* can be or'ed.

If you have set the LIBCAPS_GA_SINGLE_TRAY bit, the available (e.g. loaded) paper format is expected in the caps_ppd_base::default_paper_name. Here you must use a format name known by libpaper, because it is used to retrieve the width and length of the paper format.

static struct caps_ppd_base printer_description = {
.default_paper_name = "A4",
};
@ LIBCAPS_GA_SINGLE_TRAY
Definition: libcapsppd.h:215
Basic description of common features a printing device/printing driver can provide.
Definition: libcapsppd.h:322
enum caps_ppd_common_features common_features
Definition: libcapsppd.h:324

If the LIBCAPS_GA_SINGLE_TRAY bit isn't set, the caps_ppd_base::default_paper_name member is used to define the default paper name in the PPD PageSize list as the DefaultPageSize value.

Medium Type

The caps_ppd_base::paper_type member defines if the medium your printer prints on is of continuous form (LIBCAPS_PT_ENDLESS) like labels can be, or individual pages (LIBCAPS_PT_ENDLESS).

Note
Only one of the LIBCAPS_PT_* enums can be used at the same time.

Medium Orientation

The caps_ppd_base::default_leading_edges member is a complicated beast. Refer caps_ppd_paper_leading_edges for a description.

Note
The LIBCAPS_PLE_* can be or'ed.

Colour Spaces

The caps_ppd_base::supported_color_formats member defines the colour space(s) the printer driver supports to print.

If more than one of the LIBCAPS_CT_* enums are or'ed, the PPD will contain a choice to let the user select one of the available colour spaces. Thus, the user can force your printer driver to print a coloured image in greyscale by selecting the LIBCAPS_CT_GRAY PPD choice. Or the user can force a greyscale image to be printed with CMYK colours by selecting the LIBCAPS_CT_CMYK PPD choice instead (if you enable this colour space).

Since PPD files contain a default selection for various features, you must define a default colour space as well. For this, the caps_ppd_base::default_color_format member is used. In this member only one of the LIBCAPS_CT_* enums can be used.

Beside the feature to print in black/white or colours, a second main feature of all printers is their printing resolution. Label printers often can print in one resolution only, while many other printers have the capability to print in more than one (selectable) resolution.

The members caps_ppd_base::resolution_count, caps_ppd_base::default_resolution and caps_ppd_base::resolutions define the printer's resolution capabilities.

The caps_ppd_base::resolutions member points to an array of caps_ppd_resolution entries. Each of it defines one possible resolution capability, separated into the horizontal and vertical direction. Their values are meant in Dots Per Inch (DPI). The entries count in this array must be defined in the caps_ppd_base::resolution_count member. Since the PPD must contain a default resolution, this must be defined in the caps_ppd_base::default_resolution member, as an entry index into the caps_ppd_base::resolutions array.

The example below shows a printer which supports three different resolution settings. The first two are using the same resolution for the horizontal and vertical direction, while the third has a higher resolution only in the horizontal direction. The default resolution selection in the PPD should be the second entry on the array.

static const struct caps_ppd_resolution supported_resolutions[] = {
{ .horizontal = 300, .vertical = 300, },
{ .horizontal = 600, .vertical = 600, }, // Should be the default selection
{ .horizontal = 1200, .vertical = 600, },
};
static const struct caps_ppd_base printer_description = {
.default_resolution = 1, // e.g. 600 x 600
.resolutions = supported_resolutions,
};
static const struct caps_ppd_resolution supported_resolutions[]
Definition: caps-printing-test-provider.c:47
#define ARRAY_SIZE(x)
Definition: libcapsbase-local.h:37
size_t resolution_count
Definition: libcapsppd.h:354
Definition of one supported resolution in Dots Per Inch (DPI).
Definition: libcapsppd.h:233
unsigned horizontal
Definition: libcapsppd.h:234
Note
You can have all structures const here and only define them at compile time, but you can set them up at run-time as well (for example based on INI file settings).
Todo:
Add example.

Medium Size Restrictions

The caps_ppd_base::media_coverage member defines the printer's print size restrictions, mostly defined by mechanical limitations. For most printers it is somehow defined in their manuals, at least for the manual media feed (if available). While the maximum print size is limited to the largest media format the printer can deal with, the minimum print size is mostly defined for the manual media feed only. The caps_ppd_base::media_coverage member is used to limit the available media format entries in libpaper to the sizes the printer supports. Thus, the user can never select a paper format in the print dialogue the printer cannot handle due to its limitations.

Note
The unit to define these size limitations is points (e.g. 1/72 inch).

In this example the printer's maximum size is limited by the paper tray size (215.9 mm x 406.4 mm) and the minimum size is limited due to the mechanics to move the medium correctly inside the printer.

static const struct caps_ppd_media_size medium_limitation = {
.width_min = 198.14, // 69.9 mm
.width_max = 612.0, // 215.9 mm = "Legal"
.height_min = 328.81, // 116 mm
.height_max = 1152.0, // 406.4 mm
};
static const struct caps_ppd_base printer_description = {
[…]
.media_coverage = &medium_limitation,
[…]
};
const struct caps_ppd_media_size * media_coverage
Definition: libcapsppd.h:406
Definition of the min/max printing medium the manual feed can handle.
Definition: libcapsppd.h:242
double width_min
Definition: libcapsppd.h:243

Print Margins

The caps_ppd_base::media_margins member defines the printer's printable area and these values are used to define the ImageableArea content in the generated PPD.

Non printable margins are the second mechanical limitation of your printer. You cannot print up to the papers borders precisly on most printers. Thus, you always need some margins at all four paper borders - sometimes four different ones depending on the mechanics.

Note
The unit to define these margins is points (e.g. 1/72 inch).

In this example, the printer has mirrored margin requirements. But the manual defines different margins for portrait versus landscape print. I don't know why and how to deal with it. Currently only one set of margins is supported.

static const struct caps_ppd_media_margins printable_margins = {
.left = 17.0, // 6.01 mm, 5.0 mm portrait
.right = 17.0, // 6.01 mm, 5.0 portrait
.top = 11.9, // 4.23 mm
.bottom = 11.9, // 4.23 mm
};
static const struct caps_ppd_base printer_description = {
[…]
.media_margins = &printable_margins,
[…]
};
const struct caps_ppd_media_margins * media_margins
Definition: libcapsppd.h:414
Definition to calculate the printable area on all printing media.
Definition: libcapsppd.h:255
double left
Definition: libcapsppd.h:256

Printer Driver Specific Features

Beside shared features, there are most of the time printer specific features as well.

Visible Selectable Choices

If you want user selectable printer specific features, you can include them into the PPD as an OpenUI/CloseUI. The printing dialogues will show them and the user can chose a selection. It is a matter of taste if you want to include them into the PPD or not. Some features are more a one time configuration and never changed again.

For example my printer needs to know at what physical hight it is used: below 1000 m or above (aka "Air Pressure"). This kind of setting is (IMHO) more a one time setting, than something you need to select on a print job base.
So my printer driver reads this special setting from the driver's feature INI file and not from the PPD file. But it is up to you where you locate the printer/printer driver specific feature settings.

Two members exist to define additional PPD selections in the printer description: caps_ppd_base::selections is an array and caps_ppd_base::selection_count counts the elements in this array.

Each selection entry is divided into two parts: the base description entry caps_ppd_selection and the selections itself via caps_ppd_selection_entry.

Lets start with the caps_ppd_selection_entry which defines the selections the user can see later in his printing dialogue.

  • caps_ppd_selection_entry::option This member's value is what you will receive later on, after the user has select one entry from the list and you receive the print job. It is invisible to the user and it is up to you what value you are using here.
  • caps_ppd_selection_entry::description This member defines the visible part presented to the user in the printing dialogue. Due to this you should support Printer Driver National Language Support here.
  • caps_ppd_selection_entry::value: If the caps_ppd_selection_entry::option entry is enough for your code to do a correct configuration, it doesn't matter what you add to this corresponding member. Sometimes it could simplify the code if you add the required setting (number, string aso) to this member to retrieve the required printer specific setting in a generic manner (for example, one driver for more than one printer). There is a convienience function to retrieve this member, so it might be worth to make use of it.

In my example below I add a printer specific feature called EconoMode. It seems changing how the printer deals with its toner somehow. It starts with the selectable entries: this printer can switch this feature off entirely or can use four increments.

static const struct caps_ppd_selection_entry econo_mode_selections[] = {
{ .option = "Off", .description = NLS_("Off"), .value = "OFF", },
{ .option = "0", .description = NLS_("Value 0"), .value = "0", },
{ .option = "1", .description = NLS_("Value 1"), .value = "1", },
{ .option = "2", .description = NLS_("Value 2"), .value = "2", },
{ .option = "3", .description = NLS_("Value 3"), .value = "3", },
};
Definition of one 'option' entry a PPD selection provides.
Definition: libcapsppd.h:292
const char * option
Definition: libcapsppd.h:293

The base description for this feature follows and completes the OpenUI/CloseUI definition:

static struct caps_ppd_selection econo_mode_selection = {
.option = "EconoMode",
.description = NLS_("Toner Save Mode"),
.entry_count = ARRAY_SIZE(econo_mode_selections),
.default_entry = 0,
.entries = econo_mode_selections,
};
@ LIBCAPS_OT_PICKONE
Definition: libcapsppd.h:282
Description of an OpenUI/CloseUI PPD selection.
Definition: libcapsppd.h:303
enum caps_ppd_option_type option_type
Definition: libcapsppd.h:304

The caps_ppd_selection::option_type member defines the type of this OpenUI/CloseUI PPD entry. I have only seen LIBCAPS_OT_PICKONE yet: the user gets a list of possible selections in his print dialogue and can exactly pick one entry from this list. The also possible LIBCAPS_OT_PICKMANY or LIBCAPS_OT_BOOLEAN are good for…what?

The caps_ppd_selection::option member is the overall name for this choice. It is the key name later on, if you want to retrieve the user's selection.

The caps_ppd_selection::description member defines the description text the user will see in the printing dialogue later on. Due to this you should support Printer Driver National Language Support here.

The remaining three members caps_ppd_selection::entries, caps_ppd_selection::default_entry and caps_ppd_selection::entry_count refer the caps_ppd_selection_entry where you have already defined the possible selections.

Invisible Information

Via the members caps_ppd_base::options and caps_ppd_base::options_count more printer specific data can be included into the PPD.

One use for this feature is to define a single paper format type, if the printer has one paper tray only and there is no way for different paper formats and thus, no need to have a selectable paper format OpenUI/CloseUI in the PPD. Other usage can be to insert some printer specific values into the PPD, a generic printer driver can use to parametrize the print later on.

Note
None of the settings here are static. You can change them whenever you want. At the startup of your printer driver once, or inbetween at every time. For example the user changes the label cassette of a label printer while the label printer is online and its printer driver as well. If the printer driver can retrieve the loaded label type, it can change its paper format settings after it detects a change and the next time a client queries the PPD its content is in accordance to the current loaded label cassette.