GTK+ Printing backend HowTo



Hi,

  As I have been prompted by some in the past in writing up a
HowTo write a GTK+ Printing backend.  So this is attached, welcome
any comment and feedbacks [1], I hope to make that so that it can become
a useful document for someone who plans to write a one similar to the one
I have written for PAPI.

I also welcome suggestion as to where I can post this document once it is completed :)

thanks,

-Ghee


[1] comments from Alex and Matthias included
Title: How to write a GTK+ Printing Backend
Author: Ghee Teo <ghee teo sun com>
Date: 6th Dec 2006
Version: 0.1 (Initial Draft)
	 0.2 (Feedbacks/comments from mclasen redhat com and alexl redhat com)

I. Background

  The GTK+ Printing API is designed and implemented to replace and enhance 
  printing supports for developers. Given that GNOME applications have been 
  relied in the past on libgnomeprint/libgnomeprintui which is GNOME specific. 
  Since GTK+ have been developed and deployed not onlu on Linux/Unix but also 
  on Windows amd MacOS. It is therefore important that GTK+ printing must 
  support various backend implementations required on various platforms.

  As I have just implemented the PAPI (Printing API) as defined by Free 
  Standards Group on Solaris. I would like to share briefly even the steps 
  required to implement a GTK+ Printing backend. The following steps have been 
  described in as generic terms as possible though it still heavily influenced 
  by GTK+ printing on Unix.

II. Steps to write a GTK+ Printing Backend

The GTK+ printing backend code should reside in a sub-directory under
gtk+/modules/printbackends.

1. CVS checkout the gtk+ module from HEAD

2. Create a subdir where the GTK+ Backend module to build
   Create modules/printbackends/<backend>

3. Make GTK+ to recognise backend module to build
   Modify modules/printbackends/Makefile.am to include the module to build

4. Create Makefile.am in the <backend> subdir and modify to work
   Hints: Take a copy of these files from printbackends/lpr

5. Create the backend's backend class 
   Create a backend class named gtkprintbackend<backend>.[ch]
   This backend class is dynamically loaded g_module.

5.1 Create the G_MODULE methods that the GtkPrintBackend requires
    G_MODULE_EXPORT void pb_module_init (GTypeModule *module);
    G_MODULE_EXPORT void pb_module_exit (void);
    G_MODULE_EXPORT GtkPrintBackend *pb_module_create (void);
    Hints: check out the lpr module for the bodies of the methods.

5.2 Register the backend backend printer type in pb_module_init ()
    E.g. gtk_print_backend_<backend>_register_type (module);
         gtk_printer_<backend>_register_type (module);

    It is important to mention that the types need to be registered
    dynamically, not statically, since we are in a dlopened module.
    Ie, use g_type_module_register_type.

5.3 Create an new instance of backend type in pb_module_create ()
    E.g. return gtk_print_backend_<backend>_new ();

5.4 Write the template code of the backend class
    Hints: Use printbackends/[lpr|cups|papi] as reference points


6. Implement the GtkPrintBackend's methods
   GtkPrintBackend defines a number of interfaces that the real backend
   ought to implement to make the backend works. (see gtk/gtkprintbackend.h).
   I have listed the essential 7 below.

6.1 Method 1:  void (*request_printer_list) (GtkPrintBackend *backend);
    When the backend is created, it must dislay the list of printers that it 
    knows about into the Print Dialog. So this is the first method that will be 
    run when a backend is created. The backend should emit the 
    'printer-list-done' signal when it has complete loaded its printers.

6.2 Method 2: void (*printer_request_details) (GtkPrinter *printer);

    This method is invoked when a printer is selected as in GtkPrintUnixDialog
    (registered as a callback to the "changed" signal of the scrolled list).

    It is important to note that request_details is optional and only needed 
    when you need to asynchronously fill in printer details. The lpr backend has
    no need for that, so it just always marks printers as "has_details". 

    When the details have been filled in, the backend must set the
    has_details flag on the printer, and emit the details-acquired signal.

6.3 Method 3: GtkPrintCapabilities (*printer_get_capabilities) (GtkPrinter *)

    This is a key design of the GTK+ printing, where applications *MUST* query 
    this value and to response accordingly in how to generate its data for 
    printing. 
    Examples are: GTK_PRINT_CAPABILITY_COPIES, GTK_PRINT_CAPABILITY_PAGE_SET,
		  GTK_PRINT_CAPABILITY_COLLATE, GTK_PRINT_CAPABILITY_REVERSE, 
		  GTK_PRINT_CAPABILITY_SCALE
    (Current list are in gtkprintjob.h, but this can be extended if requires)

    NOTE: Applications only need to deal with this manually if the choose to use
    the Unix printing api. The portable api is handling most of this
    automatically.

6.4 Method 4: GtkPrinterOptionSet *(*printer_get_options) (GtkPrinter *,
							   GtkPrintSettings *,
							   GtkPageSetup *,
							   GtkPrintCapabilities);
    These two methods are invoked by the GtkPrintUnixDialog on the signal of
    "details-acquired". _gtk_printer_get_capabilities() simply return the values
    that the backend printer set.

    printer_get_options () is the real worker for getting all the options that
    the particular backend printer capable of support and display them into the
    GUI template of the GTK+ print dialog box. The backend should obtain what
    ever it support from either its print server of printer description file if
    applicable. 

    Based on the different options setting, different tab will be shown on
    the GTK+ Print dialog. Much of these information are derived from the
    PPD files for the printer.
    
    NOTE: This is the most tedious of works mapping various bits and pieces of 
    the same thing but with different terminology amid the different backends.

6.5 Method 5: void (*printer_get_settings_from_options) (GtkPrinter *printer,
							 GtkPrinterOptionSet *,
							 GtkPrintSettings *);

    This method is invoked when the Print button on the Print dialog is pressed.
    The purpose of this method is to gather from the settings on the print
    dialog that the user may have selected before the Print buttin is activated.

6.6 Method 6: void (*printer_prepare_for_print) (GtkPrinter *,
						 GtkPrintJob *, 
						 GtkPrintSettings *, 
						 GtkPageSetup *);

    After the values of the various settings have been gathered, the backend
    is now ready to set up the print-job object to be sent to the actual
    printer.

6.7 Method 7: void (*print_stream) (GtkPrintBackend *backend, 
				    GtkPrintJob     *job, 
				    GIOChannel      *data_io, 
				    GtkPrintJobCompleteFunc callback, 
				    gpointer        user_data, 
				    GDestroyNotify  dnotify);

   This is the method that does the really print job submission. Print data
   is sent over the GIOChannel. 

   The application generates the data for printing through the data_io
   stream, the backend print system read the data from here. The backend
   should call the callback function when it is done, call the dnotify on
   user_data and finally update the status of the print job.

   If the data is processed asynchronously, a ref top the print job should
   be made so as to keep the ref it alive until the print system has completed
   the processing.

7. Once you are happy with the code, compile them and install them.

8. The backend module should generate a share library in
   modules/printbackends<backend>/.libs/<myprintbackend>.so

9. The backend module should be installed into
   <prefix>/lib/gtk-2.0/2.10.x/printbackends


III. What still need to be done 

   Thre are a couple of still missing (wholely my faults)
   o there is no mention how to handle the method
     gboolean (*printer_mark_conflicts)(GtkPrinter *printer,
					GtkPrinterOptionSet *options);

     Alex has this first comment:
     It doesn't mention conflict handling, but maybe CUPS/PPDs is the only 
     system that handle that.

     My reply: This typically is the case when the user has selected a pagesize 
     that is not being supported by the printer according to its PPDs.

     Alex has this further comments:
     - That is a simple version of conflicts. But PPDs can specify much more
     interesting conflicts. For instance, if you have installed an extension
     to the printer or added more memory it can make more options availible.
     And some features conflict, like the printer might not support stapling
     when printing to some output bins, etc.

     
  o Examples to explain more on details about options and groups and how
    they work, and how the print dialog sorts options into tabs. The real
    tricky bits is to map the inconsistent naming of the attributes among
    cups, gtk+ printing, and that of PPDs.


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]