EuroBridge Widget Set Printing Support (Version 4.0)

  1. Introduction
  2. Application interface
    1. Xew Printing Interface
      1. XePrint
      2. XePrinter structure
      3. XeImaging structure
      4. XePrintPageProc callback
      5. XePrintCancel, XePrintPause, XePrintResume
    2. X Print Server interface (Reynolds version)
      1. XOpenPrintWindow
      2. XClosePrintWindow
      3. XSetPrintParams
      4. Implementation limitations
    3. Xprinter interface (Bristol Inc.)
  3. Limitations and problems of the current implementation
    1. Window clipping problem
    2. MOTIF ResizeWrapper
    3. Only one print at time
    4. Foreign Composite widgets
  4. Implementation details
    1. Printing methods
      1. Print initialize method
      2. Print method
      3. Print Constraint initialize method
      4. Print Constraint destroy method
      5. Print cancel method
      6. Print destroy method
  5. Conclusions

Introduction

Currently X Window system does not give application any guidance about how to implement printing. Each application has been forced to invent their own private solutions.

One possible solution to the printing problem might start from the following idea:

One solution is to have an X Print Server which looks like ordinary X server to the application. Rumours have it that X Consortium is planning along these lines, and there also exists an independent prototype of X Print server impelementation (posted to the net by Matthew Reynolds ).

Even though the principle is simple, application need to be aware of the printing, and has to provide a lot of "wrapper structure" to really get printing streamlined. The "idea" provides a basic frame work, but does only half of the job.

The printing support in Xew is an experimental implementation and a study how a printing based on the Xlib/X Protocol could work in Xt framework.

About the only assumption Xew makes about the printing support is that the printing occurs using standard Xlib calls and there exists something that looks like a Display structure for the printer. Thus, the Xew itself should not need (source) changes for different X Printing solutions. However, different X printing solutions need different setups in the application code. Currently, the following Xlib calls are used by the printing and must be supported by the printing solution:

For a complete printing and display integration, the system should have three different "operating modes". The current Xew printing supports only the first one:

  1. display is generated using display metrics, printout is independently generated using printer metrics and capabilities.
  2. display is generated using display metrics, printout simulates the display ("screen dump" or WYSIWYG mode),
  3. display simulates the printout, printout is generated using full printer metrics and capabilities.

In Xew the printing is based on Widget. After setting up the Print Server connection, any widget within application can be printed out with single function call (XePrint). For any non-Xew widget, printing tries to print a screen dump of the widget. But, Xew widgets are "printing aware":

When XePrint is given a widget to print, it will first relayout the widget and all its descendants for the printer in depth first order. After this, the toplevel widget is tiled into pages, and each page is printed separately by the printing process. A single widget, such as large image, may extend over several pages.

The XeText(Ed) widget as toplevel widget behaves specially: if the layout width or height (XtNcolumnWidth, XtNcolumnHeight) are not fixed, the layout will use the tiling (page) dimensions for this purpose, and the resulting new size of the TextEd widget is tiled (instead of the screen version).

Application interface

The application interface divides into sections:

Xew Printing interface

To use Xew printing in the application, the following steps are needed:

  1. Define the XePrinter structure and initialize it with the printer connection (Display pointer). Once opened, the same structure can be used with multiple printings.
  2. To print a widget, decide how it should print and set the controlling fields in the XePrinter structure appropriately. The fields can be left to ZERO values for defaults, with exception of page_width and page_height. These two values define how the widget is to be tiled into pages.
  3. call XePrint with appropriate parameters and callback function that will arrange for page changes in a printer implementation specific way.


XePrintProcess XePrint(Widget, XePrinter *, XePrintPageProc, XtPointer)

Widget w
is the widget to be printed (including all children).
XePrinter *p
is a pointer to a control structure which specifies where and how the printing should occur.
XePrintPageProc callback
is a callback function which is called once before every new page (page > 0), and once after all output to the last page has been produced (page == 0).
XtPointer client_data
is the application data passed to the callback, whenever it is called.

XePrint prints the content of the Widget (including all children) using the printing parameters given in the XePrinter structure.

XePrint returns NULL, if all printing completed within this call, and a NON-ZERO opaque printing state pointer, if the printing process is still in progress. The only way to detect the completion of the printing process in such case, is to have the XePrintPageProc defined and watch for page==0 call.

Warning!
An application should take care not to start more than one printing for a widget. The Xew is not currently prepared to do multiple simultaneous printouts from the same widget.
Note!
The current printing implementation always completes within single XePrint call, unless the application chooses to use the XePrintPause in the page callback function.

XePrinter structure

typedef struct XePrinter
    {
	Display *printer;	/* Printer connection */
	Drawable page;		/* Printer page */
	int page_width;		/* Tiling into pages, width of the tile */
	int page_height;	/* Tiling into pages, height of the tile */
	Boolean background;	/* Use widget background */
	XeImaging imaging;
    } XePrinter;
Display *printer
is the Display pointer identifying the target printer.
Drawable page
is a Drawable that will be used in all Xlib functions that require a drawable. Whether this is needed or not, depends on the printer implementation.
int page_width, page_height
define the tiling of the widget being printed (using screen/src units, pixels). Each tile printed as a separate page.
If the top level widget being printed is an XeText, and if columnWidth or columnHeight resource is undefined (has value 0), then the XeText will temporarily substitute page_width and page_height for the corresponding resource value, before doing the layout for the printer.
Boolean background
if set 'True', the background is printed. When 'False', the widget background color is set to 'white' and 'foreground' (in Xew) is black (this is most useful for XeText widget, if the application is using white text on dark background, which is not so readable combination on a printed page).
XeImaging imaging
are printer imaging controls (this mostly intended to be used if you want to "downgrade" the features). For defaults, initialize to all ZERO is sufficient.

XeImaging structure

This structure is defined in <X11/Xew/Basic.h> as

typedef struct
    {
	Boolean use_shm;
	XeColormapUse colormap_use;
	XeColorMode color_mode;
	XeDither dither;
	XeColorQuantize color_quantize;
	int max_colors;
    } XeImaging;
and the use of this is not yet fully stabilized in the printing context. Currently some of the fields can be used to change the printing behaviour. Mostly, the fields have the same specification and effect as the corresponding XeBasic resources and most effects, if any, are to the printing of XeRaster or XeVideo widgets.
Boolean use_shm
will instruct the printing to utilize MIT shared memory option for images with the X printer. Do not set this, as none of the current X printing implementations support this!
XeColormapUse colormap_use
the possible values are It is somewhat questionable what use this will have on the printer. Not significant, if the print server is in TrueColor mode.
XeColorMode color_mode
the possible values are
XeDither dither
the possible values are and the effect, if any, depends on source image (for example, currently the dither parameter has no effect on palette images).
XeColorQuantize color_quantize
the possible values are This has any effect only if the source image is TrueColor and printer shows as PseudoColor. Leave it to the default (0).
int max_colors
if > 0, defines the maximum number of colors to be used in any single image print. Perhaps useful for fancy effects with "PseudoColor" printers. Has no effect, if the printer is TrueColor. The default (0) makes printing to use the number of colors specified for the widget being printed.


void (*XePrintPageProc)(Widget, XePrintProcess, int, XtPointer)

Widget w
is the original Widget passed to the XePrint function
XePrintProcess pp
is an opaque data which identifies the internal printing state. This is used, if the callback needs to do some special control actions to the printing process using the other interface functions described later. When the callback is called by the XePrint (or by the printing process it started), this parameter is always NON-NULL.
int page
identifies the current page being printed (if > 0), and the final call after the last page, if == 0. Negative values of page are reserved for future use.
XtPointer client_data
is the client_data passed to the XePrint function.

XePrintPageProc callback is called once before every new page (page > 0), and once after all output to the last page hase been produced (page == 0).

The task of this callback is to close printing of the previous page (page > 1) and prepare for a printing of a new/next page on the printer (page > 0). The exact code depends on the X Printing support being used.

The negative values of page are reserved for future use, currently the callback should just return without doing anything for such cases. The negative values might be callbacks that would occur while printing a page, and would give the application more control over what and how the printing happens, perhaps allow changing of printing parameters depending on which child widget is being printed.

Warning!
When the callback is called with page==0, the XePrintProcess pointer should not be used for any further control calls. If the XePrint function returned with non-NULL value that got stored into some application structure, this callback must ensure that this value is not used anymore.

void XePrintCancel(XePrintProcess)
void XePrintPause(XePrintProcess)
void XePrintResume(XePrintProcess)

XePrintProcess pp
is the opaque printing process identification, either returned by the XePrint function, or the parameter of the XePrintPageProc.

XePrintCancel, XePrintPause and XePrintResume are all control functions that can be used from the XePrintPageProc, or from the application (provided that XePrint returned NON-NULL and XePrintPageProc has not yet been called with page=0)

XePrintCancel aborts the printing process as soon as possible. If the XePrintPageProc has not yet been called with the page=0 parameter, the callback occurs when the termination is completed.

XePrintPause suspends the printing process.

XePrintResume resumes the printing process suspended by XePrintPause

X Print Server interface (Reynolds version)

Matthew Reynolds implementation of the X Print Server comes with a small interface library, which implements the following functions:

In addition to these, the normal Xlib call is used in opening the printer connection. The following documentation applies to the print server code specially modified for the Xew. About the simplest printing skeleton (without any error processing), assuming Matthew Reynolds X print server modified for Xew, would look something like
#include <X11/Xew/Print.h>
#include "printlib.h"
static void PrintingPage
	(Widget w, XePrintProcess pp, int page, XtPointer data)
    {
	XePrinter *p = (XePrinter *)data;
	char page_file[80];
		if (page < 0)
		return;
	if (page != 1)
	    {
		XClosePrintWindow(p->printer, p->page);
		XFlush(p->printer);
	    }
	if (page > 0)
	    {
		sprintf(page_file, "page-%d.ps", page);
		XOpenPrintWindow(p->printer, p->page, page_file);
	    }
    }
void PrintWidget(Widget w)
    {
	static XePrinter p;

	p.page_height = 600; /* Tile into A4 size assuming 75dpi */
	p.page_width = 840;
	if (!p.printer)
	    {
		p.printer = XOpenDisplay(getenv("PRINTSERVER"));
		p.page = DefaultRootWindow (p.printer);
	    }
	XSetPrintParams (p.printer, XpScale_MAXASPECT, 1, 1,
			 p.page_width, p.page_height,
			 0, 0, NULL);
	(void)XePrint(p, &p, PrintingPage, (XtPointer)&p);
    }

XOpenPrintWindow(Display *, Drawable, char *)

Display *dpy
is the printer "display".
Drawable drawable
Currently ignored.
char *printfile
is the name of the output file (printDirect = 0), or the name of the printer device (printDirect != 0).

Start printing a new postscript output file. Creates the file or starts piping to the lpr command.

Note on printfile!
This definition of XOpenPrintWindow is rather problematic if the X printserver will be used as a shared server between multiple users. First, unless the server is run as 'root', it may not be able to create these files. Second, user may not be able to read the created files (as they will be owned by the server). Third, allowing any random filename to be specified by the client is a serious security hole. For this reason the modified for Xew print server mangles the filename as follows:
  1. only the last component of the path is used, if any given,
  2. allow only alphanumeric, '-', '.' or '_' in the name,
  3. strip all leading '.'s
  4. strip leading '-'s
The specified file will be created the to directory from which the X print server is started. As specified, the X Print Server is only usable by single user at a time, each user needs to start own private copy of the print server.

XClosePrintWindow(Display *, Drawable)

Display *dpy
is the printer "display".
Drawable drawable
Currently ignored.

Finish printing and close the output stream opened by the XOpenPrintWindow.

Note!
This function should probably include an implicit XFlush(dpy). Currently it does not, and the application needs to do an explicit XFlush after the call to get the request forwarded to the server.

XSetPrintParams(Display *, Boolean, int, int, int, int, int, int, char *)

Display *dpy
is the printer "display".
Boolean preserveAspect
controls the fitting of the printed page (output page) to the physical printing area (bounding box) on the output. The value is actually an enumeration of 5 different possibilities:
int fontScale
Ignored.
int pixmapScale
Ignored.
int pageWidth, int PageHeight
define the rectangle (output page) in pixels which will get fitted to the available printing area (bounding box) on the output paper using the method selected by the preserveAspect parameter.
int printDirect
defines whether the printfile in XOpenPrintWindow call is to be interpreted as a filename (ZERO) or as a printer name (NON-ZERO). If latter, then the ouput is piped to the Unix "lpr -P printfile" command.
int landscape
if NON-ZERO, the page rectangle (and printing output) is assumed to occur translated 90 degrees.
char *correctFile
if specified, gives the RGB correction values (see X Print Server README's for details).

When the output file is printed as is on a PostScript printer, the bounding box is the printable area on the output paper. But, with care, it is also possible to include the output inside another PostScript stream, and bounding box can be some restricted smaller area within the page.


Implementation limitations

The X Print Server code included in the Xew distribution is a modified version of the Matthew Reynolds original code (one version is available from ftp://ftp.x.org/contrib/extensions/ under name xpserv.*). The biggest functional change is the use Adobe Font Metrics (AFM) files for font metrics, instead of attempting to match the users display fonts to the printer. This fits well with Xew XeText, which already includes code that matches logical font attributes (weight, slant, size) to the fonts of the X server. Thus, having totally different font set does not hurt Xew.

Note
The AFM parser and support module only deals with 8 bit fonts. I would have added support for 16 bit fonts, if I had had any examples of such AFM files.

In addition to the above major change, many other minor changes and fixes were necessary. Only the features, that were required and used by the Xew printing code, have been tested and fixed. Anything outside this functionality is totally unguaranteed.

It would be best if the X Print server had the default visual set to TrueColor, but the support for this visual in the original code used as the base version, did not work properly. For this reason, the current Xew print server only has PseudoColor visual. This may cause images being produced with lesser color quality than would actually be necessary.


Xprinter interface (Bristol Inc.)

The detailed description of this interface is provided by the Bristol Inc. This here specifies just one example how to use the functions with the Xew printing.

Currently the Xprinter support is not working order, as I have no way of testing it myself, and test reports say it does not work (due not being able to find a Visual structure for the printer).

The most simple XePrintPageProc might be

#include <X11/Xew/Print.h>
static void PrintingPage(Widget w, XePrintProcess pp, int page, XtPointer data)
    {
	XePrinter *p = (XePrinter *)data;

	if (page < 0)
		return;	
	else if (page > 0)
	    {
		if (page != 1)
			XpPageEject(p->printer);
	    }
	else
	    {
		XpEndPage(p->printer);
		XpEndDoc(p->printer);
		XpClosePrinter(p->printer);
		p->printer = NULL;
	    }
    }
And the matching setup to print a widget would be (without any error checks!)
void PrintWidget(Widget w)
    {
	static XePrinter p;

	XpPageInfo page_info;
	double scale;

	if (!p.printer)
	    {
		p.printer = XpOpenPrinter();
		p.page = DefaultRootWindow(p.printer);
		XpMatch(XtDisplay(w), 0, p.printer);
	    }
	XpStartDoc(p.printer, "EXAMPLE");
	XpStartPage(p.printer);
	XpQueryPageSize(p.printer, XpGetPageSize(p.printer), &page_info);
	scale = XpGetScale(p.printer);
	p.page_height = XpDisplayHeight(p.printer, DefaultScreen(p.printer));
	p.page_width = XpDisplayWidth(p.printer, DefaultScreen(p.printer));
	(void)XePrint(*wp, &p, PrintingPage, (XtPointer)&p);
    }


X Consortium X Print server interface

No information available.

Limitations and problems of the current implementation

Window clipping problem

When printing widget hierarchies, the current implementation does not clip the child widget output by the parent window. If the child extends beyond the parent window, the printout may show these parts outside the parent, even if the screen display does not.

The reason for this is, that it is uncertain who should do this job: the application or the print server? It relates to the issue how (or whether) the print server should support "window concept", and how the windows should work in printing context?

MOTIF ResizeWrapper

When printing widget hiearchies, where some Xew widget is parent (usually XeFrame or XeText), the full geometry management is done for the printing. This implies calling the child resize methods. Unfortunately, if the Xew is compiled with USING_MOTIF_122, the resize methods are "wrapped" by Motif with something that requires "per display information" and cannot find it.

The current workaround is that, with USING_MOTIF_122, the resize method is not called. The end result of this is, that in such case the geometry contraints specified for the children are not necessary in effect in the printed output.

It might be possible to get rid of the "per display information" problem by using XtOpenDisplay instead of the XOpenDisplay for the printer connection, but this has not been tested.

Only one print at time

Xew widget printing code is currently prepared to handle only one active print process per widget. The safeguards against multiple active printing processes for the same widget are rather weak.

Application must not to start more than one printing process for the same widget. There is no limitation for simultanous printing of different widgets, as long as application can handle multiple asynchronous active printing processes.

The printing solution may also allow only one printing at time (with the Xew modified X print server, you would need a separate printer connection, for simultaneous printing to work).

Foreign Composite widgets

Foreign (non Xew) composite widgets are not printed.

The only way to fully print unknown widgets is to take a screen dump out of them and then move this image to the printer. This is extremely heavy and wasteful operation, as composite widgets often have no special content to print, and are mostly covered by their child widgets. For this reason, the current implementation does not print such composites, only non-composite foreign widgets are printed.

It might be sensible to add a flag to the XePrinter structure for controlling this option. Athena Tree widget is one example of a composite widget that does have useful information in the composite window itself, and where doing the screen dump routine would be desired.

Implementation details

This section will describe how the printing actually works inside the Xew (and Xt). It must be said right from the beginning that this impelementation (architecture) may not be the right one. It does work, but at the same time, it is not as simple and easy to use from widget writers viewpoint, as I would have liked.

The design goals were

and already the first goal alone causes and from the second and in general

To achieve the goals set, the following implementation strategy and printing architecture was chosen

Printing methods

The Xew printing solution adds total of 6(!) new widget methods to the XeBasic widget class structure

typedef struct _XeBasicClass
    {
	XePrintInitializeProc print_initialize;
	XePrintProc print;
	XePrintCancelProc print_cancel;
	XePrintDestroyProc print_destroy;
	XePrintConstraintInitializeProc print_constraint_initialize;
	XePrintConstraintDestroyProc print_constraint_destroy;
    } XeBasicClassPart;

For a simple widget, most of these methods might be as easy as simply calling the corresponding standard method with the cloned widget.


Print initialize method

void (*XePrintInitializeProc)(Widget, Widget, XePrintProcess)
Widget widget
is the source widget to be printed,
Widget clone
is a copy of the source widget being transformed for the printer
XePrintProcess pp
is the printing process context pointer (structure defined in PrintP.h).

is somewhat similar to widget Initialize method (called in superclass-subclass order). This should make all the necessary changes for the printing process in the clone widget instance (a typical things that need be done are recreating GC's, recomputing any pixel values, reproducing the pixmaps etc..


Print method

XtPointer (*XePrintProc)(Widget, Widget, int, int, XePrintProcess)
Widget widget
is the source widget to being printed,
Widget clone
is a copy of the source widget that has been transformed for the printer
int x, int y
specify the upper left corner of the quadrant to print in widget coordinate space.
XePrintProcess pp
is the printing process context pointer (structure defined in PrintP.h).

is a somewhat similar to widget expose method. This should do the actual printing. If this returns non-NULL context pointer, the printing is active, but not completed. In that case main process (_XePrintProcess) will exit and it is the responsibility of the widget specific printing to resume the main process by calling _XePrintProcess after the widget print has been completed.

If the widget extends across multiple pages, the print method is called once for each page.

Note on (x,y) parameter
The (x, y) parameter positions the widget content on the output space (the window attached to the clone widget). This position may be totally outside the window, and the intention is that the "expose" will actually produce the printout for the portion of the content that intersects the window.

The (x, y) is also the mechanism that the controlling print driver process uses to implement multiple "exposes" from the same widget into different pages of the output.

It would be tempting to make print method more like an ordinary expose method, and use expose event like structure and expose region parameters. Unfortunately, those structures would impose the X coordinate space restrictions (max 65535 x 65535) to the printing. The current solution allows much larger content to be safely printed.


Print Constraint initialize method

void (*XePrintConstraintInitializeProc)(Widget, Widget, XePrintProcess)
Widget widget
is the source widget to be printed,
Widget clone
is a copy of the source widget that has been transformed for the printer
XePrintProcess pp
is the printing process context pointer (structure defined in PrintP.h).

is similar to the widget Constraint Initialize method. This should make any necessary changes for the printing in the copied widget constraint record.


Print Constraint destroy method

void (*XePrintConstraintDestroyProc)(Widget, Widget, XePrintProcess)
Widget widget
is the source widget to be printed,
Widget clone
is a copy of the source widget that has been transformed for the printer
XePrintProcess pp
is the printing process context pointer (structure defined in PrintP.h).

is similar to the widget Constraint Destroy method. This should release any resources allocated in the Print Constraint Initialize.


Print cancel method

void (*XePrintCancelProc)(Widget, Widget, XePrintProcess, XtPointer)
Widget widget
is the source widget to being printed,
Widget clone
is a copy of the source widget that has been transformed for the printer
XePrintProcess pp
is the printing process context pointer (structure defined in PrintP.h).
XtPointer client_data
is the context pointer returned by the print_method.

is a method that is used to cancel the active printing process in the widget. The context parameter is the same as was returned by the print method. This function should abort whatever asynchronous printing is in progress and return the widget approximately to the state that it was before the print method call.

Note
This function should not do any tasks of the print_destroy method, which will get called later anyway.
Note
It is also guaranteed that print_destroy method is never called for a clone widget, if this printing is active on it. print_cancel is always called first in such cases.
Note
This function must not call the _XePrintProcess, which would normally happen when the active printing process completes.


Print destroy method

void (*XePrintDestroyProc)(Widget, Widget, XePrintProcess)
Widget widget
is the source widget to being printed,
Widget clone
is a copy of the source widget that has been transformed for the printer
XePrintProcess pp
is the printing process context pointer (structure defined in PrintP.h).

is similar to the widget Destroy method (called in subclass-superclass order). Its task is to release any resources allocated to the clone widget.


Conclusions

The following is a set of miscellanous comments and thoughts that have arisen from the implementation experience
© 1995 Markku Savela