The RISC OS Printing System (part 2 - The Structure)

In Part 1, I described the basic requirements of a printing system, and in this part I will show how the RISC OS printing system is designed to meet those requirements. Any programming will be shown in pseudo-Basic to try and keep it self-explanatory.

The requirements were:

This requirement is met through a module called PDriver (the sharer module). This module provides a set of SWI's (PDriver_SelectJob, PDriver_Set-PageSize, PDriver_GetRectangle, etc) to the programmer. All of these are printer independent, even though some of them do 'talk' to other modules that are designed for each type of printer (see later).

This requirement is met by claiming vectors - I will deal with this in Part 3.

This is met by using different printer driver modules, for example PDriverPS for PostScript, PDriverDP for bit image printers like dot matrix, inkjet and laser. As we saw in Part 1, many of the bit image printers have their own set of Epson escape type sequence codes which, if incorporated in the PDriverDP module, would make it too complex and inflexible. The module would have to be re-coded before a new printer could be added. So PDriverDP only takes care of the printer independent parts required by all bit image printers.

A set of modules called Printer Dumpers are provided for each style of bit image printer. For example, PDumperDM is the general Epson dot matrix dumper module providing the Epson escape sequences and PDumperLJ for the Laserjet inkjets. Other dumper modules may be added to provide for extra printers. There is one other common module for bit image printers called PDumperSupport which, as its name implies, gives extra support for common tasks required by these dumper modules (more efficient than duplicating the coding in each dumper module).

So how do these modules talk to each other when printing? To understand this, we need to look at a typical printing routine. In RISC OS, under the WIMP, printing is similar to drawing to the screen (meeting requirement 2). Study the following code used to print a typical page:

SWI Printer_Info -->
           Get the printer's requirements (What type is it? Does it need any font names? etc)

OPEN printer: -->
           This is a reserved filename to send output to printers

SWI PDriver_SelectJob -->
           This SWI sets up the printing job

IF <we need any font names> THEN
    WHILE any more fonts to find
        SWI PDriver_DeclareFont -->
                  PostScript printers require this as explained in Part 1.
    ENDWHILE
ENDIF

FOR <each page to be printed>
    REPEAT
        SWI PDriver_GiveRectangle
    UNTIL all rectangles

-->This is a bit confusing as usually the rectangle is the size of page, hence only one rectangle

-->But the page can be split into rectangles if convenient

SWI PDriver_DrawPage

-->This is where we start the printing process - this SWI gives the first area of the page to print

 WHILE <more rectangles to print>

-->Using normal graphics and printing commands, output required page

  SWI PDriver_GetRectangle -->Get the next area (rectangle) to print

 ENDWHILE

NEXT page

--> The above loop splits the page up into convenient rectangles by PDriver to print in a
similar way to printing to the screen using the WIMP - don't confuse this with SWI PDriver_GiveRectangles.

SWI PDriver_EndJob   -->End job (tidy up memory and send any required end page codes to printer etc.)

CLOSE printer:   -->Close the printer file.

All the printer SWI calls at this level are supplied by the PDriver module. This module passes on any of these calls which require printer cooperation, to the printer driver module for the selected printer (PDriverPS or PDriverDP depending which type of printer is selected). Hence the program doesn't know what type of printer is connected. Any calls that don't require printer cooperation, PDriver processes itself.


RISC OS printing system structure

Now let's look at the same coding but include any calls made by PDriver to any other modules. I will assume a bitmap image printer is selected as this is the most involved case. Refer to the diagram for the connection paths between the modules. All the above mentioned SWIs are first passed on to the PDriverDP module then to the required PDumper module as reason codes. Reason codes are not SWIs but, at this stage, can be thought of as a way to pass information to a module (more on reason codes and the passing on of SWIs in the next part).

PDriver_SelectJob calls PDumperReason_StartJob (found in the PDumper module being used for the printer). This in turn can use PDumperSupport modules PDumper_StartJob since the main job is to reserve memory.

The next time the PDumper module is called is by PDriver_DrawPage. This calls the reason code PDumperReason_StartPage to set the printer into graphics mode (escape code sequence) and then load paper and move to top of the page. The escape code sequence will be different for each PDumper module, hence this is printer type dependent.

The next time a PDumper reason code is called is after the normal graphics and printing commands. PDumperReason_ColourSet is called for colour matching. This reason code can use PDumperSupport_SetColour SWI to do the required colour matching.

Then PDriver_GetRectangle calls PDumperReason_OutputDump; this reason code produces the required escape sequence codes to produce the output. The input for this reason code is in the form of an image strip whose depth varies depending on the number of pins/inkjets in the head of the printer. The PDumper then converts this strip into the required set of printer codes to produce the strip on the paper. This is then repeated until the last strip has been printed. When the last strip has been printed, a set of PDumper reason codes are called to tidy up the workspace memory, close the job (reset printer eject page etc) and then start a new page ready for next page (if there is one).

These reason codes are:

DumperReason_EndPage
PDumperReason_AbortJob
PDumperReason_StartPage

If there are no more pages to print, the final PDriver_EndJob SWI calls PDumperReason_AbortJob.

I know this sounds very complicated but the main idea is to show the different control layers present.

Each PDumper reason code provides the printer dependent escape codes. There are many types of inkjet/dot matrix printers that use slightly different codes but use the same overall code structure, so rather than producing a PDumper module for every printer, it is simpler to provide a means of telling the PDumper module(s) which codes are required. This is done by the PrintEdit application which I will deal with in Part 4. If a newer/better coding is found for a printer (e.g. better dithering of colours for graphics), or a new printer requiring a different code structure is to be added, then a new PDumper module is needed. This can be added to the structure without the need to change any of the other modules.

For PostScript printing, all the required SWI codes are passed on by the PDriver module to the PDriverPS module - no dumper or support modules are used since the PostScript language is the only output required. PDriverPS also takes care of any font processing.

I think this is all for this part. Next time, I will look at how the normal screen writing, graphic and printing commands can be redirected and modified for printing, how SWIs are passed on and how reason codes work.


Source: Archive Magazine 13.5
Publication: Archive Magazine
Contributor: Brian Pickard