[Gs-devel] PDF 1.4 driver activation issues
raph at acm.org
Mon Feb 26 18:19:09 PST 2001
Hi Ghostscript developers,
Some of you have no doubt checked out the PDF 1.4 support under
development in Ghostscript. At the moment, PDF 1.4 rendering happens
only in the pnga device, to which it's glued. Obviously, it's
important for PDF 1.4 to work with all devices. This note raises some
relevant architectural issues toward that end. Comments and discussion
are more than welcome.
One design decision made early on is that support for PDF 1.4 is to
be concentrated into a single device. This is largely because PDF 1.4
is such a radical rework of the PostScript imaging model. Trying to
extend the Ghostscript device interface to support the entire PDF 1.4
imaging model would be quite difficult, to say the least. If it were a
simple matter of doing alpha compositing over the target buffer, then
the existing get_bits_rectangle interface as used in gsalphac.c would
be sufficient. However, PDF 1.4 requires, in general, for the
destination buffer to have target alpha. In addition, up to two alpha
channels are required to properly support knockout groups. Finally,
rendering a PDF 1.4 document requires lots of temporary buffers. In
the present architecture, these are managed within the PDF 1.4 device,
in response to begin_transparency_group and friends.
On the converse, while for the PostScript imaging model, much of
the flexibility in allowing different implementations of the basic
paint operators is useful, it isn't for PDF 1.4. In particular, with
the "cut and stencil" model of PostScript, halftoning distributes
through composition - you can either composite first and then
halftone, or halftone and composite into a halftoned buffer, and
you'll get the same results. That's not the case whenever you have any
form of transparency. Thus, note that Ghostscript's existing alpha
compositing support, in the Next DPS extensions, does not work with
So, how do we make this work with other drivers? The generic
solution is to chain devices, a technique already much used in
Ghostscript. Thus, all imaging operations will go to the PDF 1.4
device, which will then render them and send the resulting bitmap to
the real target.
This raises several questions:
* How does this chain get built?
* What happens when the target device has alpha?
* How do we deal with banding?
I'll deal with each of these in turn.
How to build the chain
I feel pretty strongly that the chain should be built explicitly
by the PDF main code, when it detects the presence of PDF 1.4
operators (the code to do this detection is already in place). Peter's
original design had the chain being built "by magic" when the first
begin_transparency_group call was invoked on the target device.
However, this places the onus on each device to be able to interpose
the PDF 1.4 device before itself in response to this call. To my
taste, that makes Ghostscript's device architecture even more tangled,
which is a step in the wrong direction.
Rather, I'm strongly leaning toward a model in which two devices
are exported to the PostScript language layer. Peter calls these the
"real" and "effective" devices in analogy to Unix uid's, but I feel
these are confusing and would rather call them the "physical" and
"filter" devices. Of course, if you have a better proposal for naming,
I'd love to hear it.
In this model, all imaging operations go to the filter device,
which is instantiated with the physical device as a target. My feeling
is to make the .device field of the gs_state structure refer to the
filter device, and add a .physical_device field, as the majority of
references to the current device will deal with the filter device.
By default, the filter device is set equal to the physical device.
The PDF main code will call a routine that creates the PDF 1.4 device
with the physical device as a target, and install the newly created
device as the filter device. The PDF 1.4 device could then remove
itself upon end of page.
One reason I like this architecture is that it is useful for other
applications besides PDF 1.4. In particular, it could be used to
implement PDF 1.3 overprint and overprint mode functionality, using
get_bits as necessary. Again, the PDF code would explicitly interpose
the device when these parameters are set in the graphics state.
What happens when the target device has alpha?
In the generic case, the PDF 1.4 sends the rendered page to the
target device by emulating the PostScript colorimage operator. This
guarantees that it will work with all devices, even when halftoning is
enabled. However, colorimage lacks the power to express an image with
an associated alpha channel, as would be needed by a PNG image in RGBA
format, for example.
Clearly, some mechanism is required to send data with an alpha
channel to the target device. I'll give one proposal below.
How to deal with banding
Banding is tricky. I consider the current code for activating the
banding logic to be messy. It's possible to simply hack it to make it
PDF 1.4-aware, but I wouldn't mind cleaning it up a bit in the
Currently, banding is invoked primarily from the printer device.
Basically, a printer device contains the union of a memory device and
a command list device, with some extra fields. On setup,
gdev_prn_allocate attempts to allocate a buffer large enough to hold
the entire page. If this succeeds, it initializes the device as a
memory device. If it fails, it initializes the device as a command
To my mind, this kind of dynamic reassigment of the "class" of the
device object is the object-oriented equivalent of unstructured
programming or even self-modifying code.
I believe that a much cleaner approach would be to have the printer
device be a forwarding device. The gdev_prn_allocate function would
then create a (pure) memory or clist device based on the same policy
it implements now, then set this device as the target.
PDF 1.4 in banded mode could then be implemented as follows. The
printer device is "PDF 1.4 aware", probably by implementing a newly
created device procedure for probing this. Then, the explicit call to
create a PDF 1.4 device from the PDF main code has slightly different
behavior: it tests whether the target device is PDF aware. If so, then
it does nothing, expecting the target device to successfully handle
PDF 1.4 extensions. If not, then it interposes the generic PDF 1.4
device as above.
When the printer device learns that PDF 1.4 is required, it then
attempts to allocate the appropriate buffers (possibly much larger
than the pre-1.4 case). If this succeeds, then it creates a generic
PDF 1.4 device, but without the mechanism to emulate colorimage to
deliver the rendered page to the target. Rather, the PDF 1.4 device
implements the get_bits() call to get the bits (with alpha channel
"flattened") directly from the buffer. Recall that printer devices
usually work by repeatedly calling gdev_prn_get_bits().
If the allocation fails, then the printer device sets up a clist
device much the same as it does now, but uses the PDF 1.4 information
to decide the buffer sizes. The target of the clist is the PDF 1.4
device. However, the get_bits() call of the PDF 1.4 device is now set
up to play back the clist, rendering into the band buffer, as
Finally, this architecture offers a solution to providing alpha
without having to muck with colorimage - add a new get_bits_alpha
I find myself uneasy with overloading the device object to have so
many functions. To me, it feels like there are multiple different
interfaces being implemented in the same class. In particular, the
printer device functions as both a target for rendering operations and
a source of bits in response to get_bits(), for the client printer
driver code. There are a lot of cases, particularly banding, in which
it makes sense for these to be two separate objects. Also, we're
adding a get_bits_alpha method which _only_ makes sense as an image
source for printer-like devices, which suggests that there should be a
new class of objects for this type of image source. Any strong
feelings pro or con?
Comments or questions about any of the issues I've raised here are
More information about the gs-devel