<aside> 🌐 https://ghostscript.com/r/The-Great-Device-Rework-Of-2021

</aside>

Background

The Device Interface

Central to Ghostscript's operation is the concept of a Device. Each Device, put simply, encapsulated the ability for Ghostscript to output to a particular device and/or format.

Ghostscript calls every device across a series of functions known as "the device interface". These vary from being very simple (e.g. "Fill a rectangle in this color") to much more complicated (e.g. "Handle an image").

Almost every device function has a 'default' implementation, which ends up calling down to simpler device calls. Thus, each device doesn't need to implement every function; if the (relatively small set of) simple functions are handled, the more complicated ones will work too.

The simpler ones are referred to as 'low-level', the more complicated ones as 'higher-level'.

Some devices are free to implement the higher-level ones to enable more advanced features; for example, the pdfwrite device, by capturing higher-level ones, can keep more of the graphical objects intact - i.e. images are transferred through unchanged, rather than being broken down into rectangles.

In a nod to the object-oriented style of programming that we ape in C, we refer to these device interface functions as being 'methods'.

Why does it need changing?

Traditionally, we have been very reluctant to make changes to the device interface (beyond adding new methods), as any change we make can require changes in existing devices. Even a simple tweak to an existing method like adding an argument multiplies up to be a lot of work, because every device (or at least every device that implements that method) needs to be updated to match.

We have a whole range of devices, from many different authors, of many different vintages. Our customers have devices too, including many for which we do not have access to the source code.

To make matters worse, we don't have access to every printer that our software drives; testing that we haven't broken a device can at best be done by printing jobs to file using old and new versions and checking that the bitstreams agree.

Accordingly, for years, we've restricted ourselves to only ever adding new methods (which can be mapped down to the existing ones by new 'default' code). Where methods become obsolete, we can mark them as such, but can never remove them from the list for fear of breaking existing devices.

So what has changed?

Well, nothing radical has changed, but it's got to the point where we are carrying around a large number of obsolete functions that are simply never called. Similarly, there are some device methods that have to work quite hard to cope with the fact that they don't quite have all the information they'd like. Finally, there are some things we'd like to do with Ghostscript that would be much harder with the device interface as it is now, than it would be with some spring cleaning applied.

Put simply, we now believe that the balance of benefits vs work justifies us revamping the device interface.

Wait a second. I'm reading this in 2032. When is "now"?

We've just released 9.54.0. We expect the device interface changes to go in before the next release.

What has changed?