[Desktop_printing] Re: Trying out sample implementation of japanese OpenPrinting efforts

Till Kamppeter till.kamppeter at gmx.net
Mon May 15 11:39:54 PDT 2006

[ Re-posting previous message without attachments, as Intel does not
  allow this kind of attachments, providing files on linuxprinting.org
  instead. ]

TORATANI Yasumasa wrote:
> At the present, we have the PDAPI document at;
> ftp://ftp.pwg.org/pub/pwg/fsg/vector/pdapi-spec-1.0rc1.pdf
> And developers can see the driver skeleton code named "opvpnull" at;
> http://sourceforge.jp/projects/opfc/
> This driver can be worked as a shared library type driver, and accept all driver
> function calls from the caller, such as Ghostscript, and shows all the
> parameters of each function calling, but do nothing except show the message.
> Please see the "INSTALL" file to know how to show the messages, and use
> the script "test/gstest.sh" for testing the driver code as following.
> $ cd opvpnull/test
> $ ./gstest.sh  example.ps

Thank you very much for your answer.

I have tried it out and opvpnull-0.0.1 works for me without patches, but
the supplied opvpnull-0.0.1 only contains the library directly linking
to the glue code, no RPC version.

So I decided to add the RPC code to the package and I succeeded without
adding any driver specific code, which means that programming the driver
itself is not dependent on whether it will be directly linked as a
dynamic to the glue code (and so run in the same process as the
renderer) or whether it will be accessed via RPC (driver runs in extra

The result of my work you find in opvpnull-
and my changes on the old opvpnull-0.0.1 in
All added files are from the libopvp
package in the CVS of the opfc project on SourceForge.jp

My new package builds always a dynamic library of the driver:
/usr/lib/libopvp_null.so.0.0.1 and it builds always files to use the
driver via RPC: /usr/lib/opvp_rpc_null.so.0.0.1 and
/usr/bin/opvp_rpc_null_server. You can choose by using




whether /usr/bin/opvp_rpc_null_server will have the driver statically
linked or whether it links the driver library
/usr/lib/libopvp_null.so.0.0.1 dynamically.

You can test the thingy as before by changing into the test/ directory
and running

./gstest.sh  example.ps

(you can replace the PostScript file by another one if you want).

This works now without "make install" (so you can test without switching
to root and without modifying the system) and it runs the test twice,
once with directly linked driver and once with driver in separate
process. The new files x1 and x2 then show the "time" output of the two
tests and in case of "-DDEBUG_PRINT" being set in the "AM_CFLAGS" line
in Makefile.am also the driver functions called with their arguments. So
you compare can compare the run times and also the drawing workflow
(which should be equal).

Do always

make maintainer-clean
./autogen.sh --prefix=/usr

after doing changes, especially after changes in configure.in or

Here are some observations, bugs, and questions:

--> BUG: "make maintainer-clean" should remove ALL machine-generated

In opvpnull it behaves like only doing "make distclean". Perhaps this
can be also a bug of automake (I use 1.9.4) or a missing command line
option for automake in autogen.sh.

--> Redundant binary code installed when the driver is installed on the

Lets have a look into the new Makefile.am, to see on what the three
binary files which are used when running the driver depend:

##  We build two convenience libraries that provide the bulk of each
##  RPC server executable.  One is geared to servers that dlopen the
##  module they need, the other is for servers that link statically
##  with their module.  Modules for the latter should be built as a
##  convenience library (noinst_LTLIBRARIES) as well.
##  There only needs to be a single dlopen based server (because it
##  opens the module passed via the command line that is exec'd by
##  the Ghostscript driver).

server_sources = \
	opvp_common.h \
	opvp_driver.h \
	opvp_rpc_core.c \
	opvp_rpc_core.h \
	opvp_rpc_reqno.h \
	opvp_rpc_server.c \
	opvp_rpc_server.h \

noinst_LTLIBRARIES = \
	lib_dlopen_server.la \

lib_dlopen_server_la_SOURCES = $(server_sources)
lib_static_server_la_SOURCES = $(server_sources)
lib_dlopen_server_la_CPPFLAGS = -DDLOPEN_MODULE
lib_static_server_la_CPPFLAGS =

##  The following part illustrates how to build all the parts needed
##  to get a null printer to work via an RPC server.

bin_PROGRAMS = \

opvp_rpc_null_server_LDADD = lib_dlopen_server.la
opvp_rpc_null_server_LDADD = lib_static_server.la libopvp_null.la

##  We have no sources, but automake defaults to the program name with
##  a '.c' appended if we don't explicitly tell it.
opvp_rpc_null_server_SOURCES =

	libopvp_null.la \

##  Note that libtool mutters that the -version-info flag is ignored
##  for convenience libraries, but for a real module it is required.
libopvp_null_la_LDFLAGS = -module -version-info 0:1:0
libopvp_null_la_LDFLAGS =
libopvp_null_la_SOURCES = \
	opvp_media.defs \
	opvp_null.c \
	opvp_null.h \
	opvp_common.h \

##  The Ghostscript driver contain its own _init and _fini functions,
##  an obsolete and dangerous practice that *should* be fixed in the
##  sources.  For now we cater to it by passing -Wc,-nostdlib to the
##  link stage.  This results in libtool passing the correct option,
##  -nostdlib to the compiler.
##  See the Program Library HOWTO, section 5.2 for details regarding
##  the use of _init and _fini.
opvp_rpc_null_la_CPPFLAGS = -DDRVNAME=\"opvp_null\" \
opvp_rpc_null_la_LDFLAGS = -Wc,-nostdlib -module -version-info 0:1:0
opvp_rpc_null_la_SOURCES = \
	opvp_rpc_client.c \
	opvp_rpc_client.h \
	opvp_common.h \
	opvp_rpc_core.c \
	opvp_rpc_core.h \

#AM_CFLAGS = -Wall -fPIC -O2

AM_LDFLAGS = -lm	## FIXME: pdapi.h uses floor in some macros
			##        that it should not provide in the
			##        first place

(1) The dynamic library of the driver itself,
/usr/lib/libopvp_null.so.0.0.1 is generated by the definition
"lib_LTLIBRARIES = libopvp_null.la" and as expected it depends on the
driver code (opvp_null.c and opvp_null.h in "libopvp_null_la_SOURCES").

(2) The server executable with which the driver is run in a process
separate from the renderer, the file /usr/bin/opvp_rpc_null_server, is
completely independent of the driver code (no opvp_null.c and
opvp_null.h in the sources and driver name or parameters not written
into the code) for the case that the driver is dynamically linked, so
one should better ship it with the glue code, as the file
/usr/bin/opvp_rpc_server, but give to it another license than the GPL,
so that it is allowed to dynamically link closed-source drivers with it.
In the case of a statically linked driver the binary code of
/usr/bin/opvp_rpc_null_server is repeatedly contained in every driver,
so that a dynamically linked driver should be preferred, as then one can
have one /usr/bin/opvp_rpc_server for all drivers. But driver-specific
server executables still make sense for closed-source driver libraries
do not link to the system's /usr/bin/opvp_rpc_server due to binary
incompatibility. These driver-specific server executables should reside
as files /usr/bin/opvp_rpc_<driver>_server.

(3) If a driver accessed via RPC is dynamically linked to the server
executable, the driver library itself is identicall to a driver library
which is directly linked by the renderer. This means that the driver
developer does not need to care whether his driver will be linked
directly or run in a separate process while he is writing the driver.
The decision how the driver is run can be done later and depends only on
the license of the driver.

(4) The dynamic library which is linked directly to the renderer when
the driver is run in a separate process, file
/usr/lib/opvp_rpc_null.so.0.0.1, does not contain any source file which
is driver dependent and its only driver-dependent part is the driver
name contained in it (from 'opvp_rpc_null_la_CPPFLAGS =
-DDRVNAME=\"opvp_null\" -DSERVERNAME=\"opvp_rpc_null_server\"'
definition in the Makefile.am). So this library can be made independent
of the driver by supplying the driver name on the renderer command line
(library can then be named /usr/lib/opvp_rpc.so.0.0.1 for example and
shipped with the glue code or directly incorporated into the glue code).

(5) Applying the improvement suggestions (2)-(4) a free driver package
only needs to ship the dynamic library for direct linking and can be
both directly linked or run in a separate process. The latter would be
for free but GPL-incompatible code. A closed-source driver should be
statically linked to its server executable or bring its own server
executable to avoid the binary compatibilty requirement to the system's
/usr/bin/opvp_rpc_server, but it should always have the improvement (4),
not shipping its own /usr/lib/opvp_rpc_<driver>.so.0.0.1 as otherwise
this library needs to be binary-compatible to the renderer or the user
would be required to compile it (and many systems have no compilers
installed by default).

(6) One could make the glue code somewhat more intelligent so that it
auto-detects whether the driver can be directly linked or run in a
separate process. Important for that is consistent naming of the files:

Driver name is <driver>

Driver library (not needed when driver statically linked into server
executable) is


Server executable (optional for free, required for closed-source driver) is


Renderer command line is then:

gs -r600 -sDEVICE=opvp -sDriver=opvp_<driver> -sModel="<modelname>"
-sJobInfo="<job info>" -dBATCH -dSAFER -dQUIET -dNOPAUSE -sOutputFile=- -

or somewhat simplified

gs -r600 -sDEVICE=opvp -sDriver=<driver> -sModel="<modelname>"
-sJobInfo="<job info>" -dBATCH -dSAFER -dQUIET -dNOPAUSE -sOutputFile=- -

The former is the same command line as for direct linking the driver.

Now the glue code could check the presence and the linkability of the
driver library. If it succeeds to link it, it works with directly linked
driver. If it does not succeed, it looks for the driver's server
executable and runs it. If there is no server executable but a driver
library which the renderer cannot link with or is not allowed to link
with, it calls its own generic server executable and lets this one with
the driver library.

Please tell me whether these suggested improvements are feasable and if
not, why not. Especially I would appreciate very much if we could do
away with the /usr/lib/opvp_rpc_<driver>.so.0.0.1.

> On the other hand, we do not have a development or implementation guide like
> HOWTO at the moment, and we agree that it helps us to understand easily to
> know the way of the driver development, so, we will discuss what and how to
> prepare the document on the printing japan mailing list and at the next
> OpenPrinting WG Japan f2f meeting.

I am looking forward for the HOWTO coming out, but it seems that the
PDAPI doc (ftp://ftp.pwg.org/pub/pwg/fsg/vector/pdapi-spec-1.0rc1.pdf)
plus the new opvpnull- package already serve well, as one does
not need to program the server executable explicitly. One simply fills
in all functions in opvp_null.c so that they produce the graphical
primitives in the printer's language, puts NULLs into the functions in
the function list if one has not filled in the appropriate function in
opvp_null.c, and replaces all "null"s in file and variable  names and
comments by "<driver>"s.

>>I also would like to know whether the OpenPrinting vector driver has a
>>way to cleanly cancel jobs. Is the job cleanly shut down when sending
>>SIGTERM to GhostScript?
> Currently, it depends on the driver side implementation.
> If the driver needs the signal handing feature for particular signal code, driver
> has to prepare the signal hander for doing it. If the driver is a RPC type one
> and needs to deal with some kind of signal, both the client and server driver
> may have the signal handler.

Can we add a function for this into the API (which is called by the glue
code when the renderer receives SIGTERM)? It would be very useful if the
driver runs in a separate process. So a SIGTERM to the renderer could
call this function in the glue code and then an appropriate command gets
via RPC to the driver. And if the driver is linked directly nothing else
than the function being called directly happens, as with any other
driver function. This way the driver shuts cleanly down without any
extra code with renderer wrapper (CUPS filter) needing to find out what
the driver process is.


More information about the Printing-summit mailing list