C for Yourself - part 47

Defining rectangles and catching errors - Steve Mumford continues his printing tutorial

Once you've worked your way through the message-passing minefield described last month, the next step is to open a file channel to the printer and tell the driver software where to place the material on the page. Several methods of opening files for output are available to us, but there are one or two things to remember.

You might be familiar with the use of the FILE structure, together with functions such as fopen() and fclose(), and these can be used to open files easily:

FILE *fptr;
fptr = fopen("printer:", "w+b");

Assuming the operation had been a success (fptr would be set to NULL if it hadn't), the variable fptr would point to a FILE structure containing various pertinent bits of information about the channel we'd opened. However, the PDriver SWI calls used to select and control print jobs require a simpler representation - the raw file handle can be obtained by interrogating the structure and reading the integer variable __file stored within:

job = fptr->__file;

Once you have the file handle, the PDriver_SelectJob SWI can be called, after storing the handle in RO and puffing a pointer to an identifying string of text in R1. This will suspend the currently active job, if one exists, and transfer that status to your own. It's at this point that plotting calls begin to be intercepted, so avoiding errors is essential - more on which later.

The next stage is to define an area of your document that you wish to print, along with its intended position on the paper, and this is done using the PDriver_GiveRectangle call. In a situation where the user wanted to print a simple letter, one rectangle would be defined, covering the entire sheet. However, it's perfectly possible to define several rectangles on one physical page, and this might be used when printing sheets of labels or reducing several pages to fit onto one sheet of paper.

On entry, R0 should hold a numeric identifier for the rectangle being defined - it's not important, but it might help you keep track if you've set up a whole stack of them. R1 points to a four-word block of memory holding the minimum and maximum x and y coordinates of the area of workspace to be plotted, stored in OS units - for instance, the following structure would hold the values in the correct order:

typedef struct
  int xmin, ymin, xmax, ynax;
} BBox;

_kernel_swi_regs in;
BBox box;

box.xmin = 0;
box.ymin = 0;
box.xmax = Oxffff;
box.ymax = Oxffff;
in.r[1] = (int) &box;

R2 points to another four-word block that contains a transformation matrix, allowing the user to scale and rotate a rectangle as it's printed. Due to the units used, in order to transfer a rectangle to the page without altering its size or orientation, the four words should contain the values 1<<16, 0, 0 and 1<<16 in that order. I'll go into this in more detail later.

R3 points to a two-word block containing the x and y millipoint offsets of the rectangle you wish to print from the bottom left corner of the paper - in simple cases, these will both be zero, Finally, R4 holds the background colour of the rectangle, stored as red, green and blue values from 0 to 255, put together in the hexadecimal form &BBGGRROO.

That's the hard work out of the way; from here it's very similar to the standard WIMP redrawing loop we know and love. However, before I fill in the details, it's important that I reveal one of the pitfalls of this printing technique.

Error trapping

The process of sending graphical data to the printer is somewhat fragile, and error cascades are easily created. With this in mind, there are several 'escape' functions that allow you to kill the print job in its tracks - however, we need to know how to catch those errors in the first place.

Acorn's standard kernel.h header file declares a structure with the name _kernel_oserror, which can be used to collect and hold information from a SWI call that's returned an error. The structure has space for an integer error number, and a 252-byte character array for the actual error message, and it might be used in the following manner:

_kernel_oserror *ERR = NULL;
ERR = _kernel_swi(PDriver_DrawPage, &in, &out);
if (ERR != NULL)
  /* An error has occurred - deal with it here */

The _kernel swi() function will usually return the value NULL if all has proceeded according to plan; if this isn't the case, something has gone wrong and it's time to take evasive action. Calls exist to allow the programmer to abort or cancel print jobs, and I'll provide you with more information next month.

Source: Acorn User - 191 - February 1998
Publication: Acorn User
Contributor: Steve Mumford