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:
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":
XeText,XeTextEd
perform a total
relayout using the fonts and metrics provided by the print server, and
generate the text directly to the print server. This is not a
screen dump, and means that even almost unreadable text on the display
is readable on the printout. While printing is in progress,
expose events to the original XeText widget content and editing in
XeTextEd are disabled (actually, while printing, the content of the
original XeText is empty!.
XeRaster, XeVideo
generate the
print image from the current source data (in case of video, this is
the current frame being shown).
XeFrame
performs a geometry layout
using the "printer changed" versions of the child widgets. This
means that if the child geometries were changed due to printer
requirements, the XeFrame will adjust to them.
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).
The application interface divides into sections:
To use Xew printing in the application, the following steps are needed:
XePrinter
structure and initialize it
with the printer connection (Display pointer). Once opened, the same
structure can be used with multiple printings.
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.
XePrint
with appropriate parameters and
callback function that will arrange for page changes in a printer
implementation specific way.
Widget w
XePrinter *p
XePrintPageProc
callback
XtPointer client_data
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.
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
Drawable page
int page_width, page_height
Boolean background
XeImaging imaging
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
XeColormapUse colormap_use
XeColormapUse_SHARED
(default)
XeColormapUse_SHAREDOWN
XeColormapUse_OPTIONAL
XeColormapUse_PRIVATE
XeColorMode color_mode
XeColorMode_NONE
, use same mode as specified for the
widget (default)
XeColorMode_MONO
, convert all images to black and
white (bitmaps), before sending them to the printer,
XeColorMode_GRAY
, convert all color images to
grayscale, before sending to the printer,
XeColorMode_COLOR
, use colors on printer (basicly,
send images as they are to the printer, colors included).
XeDither dither
XeDither_NONE
XeDither_FS4
XeColorQuantize color_quantize
XeColorQuantize_FAST
(default)
XeColorQuantize_HECKBERT
XeColorQuantize_PPMQUANT
int max_colors
Widget w
XePrint
function
XePrintProcess pp
int page
page
are reserved for future use.
XtPointer client_data
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.
XePrintProcess pp
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
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); }
Display *dpy
Drawable drawable
char *printfile
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.
printfile
!
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:
Display *dpy
Drawable drawable
Finish printing and close the output stream opened by the
XOpenPrintWindow
.
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.
Display *dpy
Boolean preserveAspect
XpScale_MAXASPECT
, the output page is scaled to the
bounding box and aspect ratio is preserved,
XpScale_FITAREA
, the output page is exactly fit to
the bounding box, changing the aspect ratio as needed,
XpScale_WIDTH
, the output width is scaled to the
width of the bounding box, and the height is computed so that the
aspect ratio is preserved (possibly causing the output to extend
outside the clipping box),
XpScale_HEIGHT
, the output height is scaled to the
height of the bounding box, and width is computed so that the aspect
ratio is preserved (possibly causing the output to extend outside the
clipping box),
XpScale_NONE
, the current bounding box has no effect
on the size (possibly causing the output page to get clipped both from
right and bottom)
int fontScale
int pixmapScale
int pageWidth, int PageHeight
preserveAspect
parameter.
int printDirect
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
char *correctFile
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.
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.
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.
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); }
No information available.
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?
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.
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 (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.
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
To achieve the goals set, the following implementation strategy and printing architecture was chosen
the XeBasic
widget class is
enhanced with printing methods.
to have a place to store the printer modified resources, it was decided to make a copy of the each widget structure for the duration of the printing process. The cloned widget will have the core.screen pointing to the "printer screen" and core.window contains the "page window" on the printer.
the XePrint
handles the building of the cloned widget
tree, transforms the core fields directly and arranges calls to the
widgets print initialize methods for the subclasses, when available
(XeBasic subclasses).
after initialization, the top level widget is tiled into pages, and the cloned widget tree is traversed top down once for each page, printing each widget on the way either by the print method, or by a screen dump for foreign widgets.
after printing is complete, the cloned tree is traversed again and all resources are released by calling the appropriate print destroy methods, and finally the clone tree itself is released.
The Xew printing solution adds total of 6(!) new widget methods to the XeBasic widget class structure
XePrintInitializeProc
)
XePrintProc
)
XePrintDestroyProc
)
XePrintCancelProc
)
XePrintConstraintInitializeProc
)
XePrintConstraintDestroyProc
)
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.
void (*XePrintInitializeProc)(Widget, Widget,
XePrintProcess)
Widget widget
Widget clone
XePrintProcess pp
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..
XtPointer (*XePrintProc)(Widget, Widget, int, int,
XePrintProcess)
Widget widget
Widget clone
int x, int y
XePrintProcess pp
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.
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.
void (*XePrintConstraintInitializeProc)(Widget,
Widget, XePrintProcess)
Widget widget
Widget clone
XePrintProcess pp
is similar to the widget Constraint Initialize method. This should make any necessary changes for the printing in the copied widget constraint record.
void (*XePrintConstraintDestroyProc)(Widget,
Widget, XePrintProcess)
Widget widget
Widget clone
XePrintProcess pp
is similar to the widget Constraint Destroy method. This should release any resources allocated in the Print Constraint Initialize.
void (*XePrintCancelProc)(Widget, Widget,
XePrintProcess, XtPointer)
Widget widget
Widget clone
XePrintProcess pp
XtPointer client_data
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.
void (*XePrintDestroyProc)(Widget, Widget,
XePrintProcess)
Widget widget
Widget clone
XePrintProcess pp
is similar to the widget Destroy method (called in subclass-superclass order). Its task is to release any resources allocated to the clone widget.
The decision to build a cloned shadow widget tree is rather drastic one. The current implementation does not attempt to build a fully functioning widget tree, the current result is rather delicate and works only because the Xew printing methods attempt not to trigger any dangerous Xt tree traversing actions (such as geometry management). If the "clone tree solution" is used in the future, the building will probably need more work.
A related issue: current implementation is not prepared to handle multiple prints on same widget at same time, and printing is only loosely safeguarded against the source widget being destroyed or otherwise modified during the print process. Beware!.
If printing in Xt environment is further developed, instead of directly defining the methods in the widget class, printing should perhaps be defined as an extension record to the core class.
The "job description" of the print methods themselves needs some examination. Although, in principle they resemble their normal counterparts, in practise somewhat ugly compromises had to be made (for example, print_initialize of XeText needs to do partially the same job as is done in the print constraint initiliaze). This is an indication of some design flaws, which need some furher thought.
Neither of the test printing implementations (Xprinter or X Print server) use Window concept, and as a result, the printing process has to arrange for the relocation and clipping (not done currently) of the widget output. The implementation might become easier and cleaner if it would be possible to create own windows on the print server for each widget in the cloned tree. On the other hand, this would make the cloning process even heavier operation. If "printer windows" are implemented, some consideration of their function is needed. It seems likely that the window on printer should only provide coordinate translation and clipping, anything else, like automatic border drawing is problematic.
The printing uses the coordinate system defined by the screen. This is a serious drawback, especially on the text output. All widths are computed in screen units, which is way too coarse for a quality output. It would be desirable to use higher resolution on the printer (say 600dpi), but this would require much more work and care on building the cloned tree and initializing it to the printer, because all resources and internal state variables containing any dimension related values would need to be scaled (font sizes, geometry constraints, positions, widget sizes). This is doable, but was beyond the resources of this experimental implementation. Additionally, high resolution might cause overflows in many X fields that are defined as Position or Dimension.
Printed images are sent to printer scaled to the same dimensions as are shown in the display. Again, this is a loss, if the printer could do better than display. Having the print server to support XIE (image extension) might be a solution for this problem. Then, the printing would have an option to send the original source image to the printer, and define the desired output through the XIE operations (leaving the scaling, dithering, etc to the printer).