RISC OS Printing System : How it works
In the olden days, when the dragon still lived in the snow-topped mountains, each application program had to understand each type of printer. So your copy of First Word Plus came with fifty printer drivers, and your copy of Hearsay came with three (which were optimistically supposed to cover the three generic types of printer in existence), and other programs had their own drivers.
With the advent of RISC OS, Acorn took a bold leap forward: since programs could render their output to the screen, they decided, then if printing worked in the same way as the screen, all they'd have to do would be to render in the normal way, and the operating system could handle the printer-dependent side of things.
This is a great simplification because you only need one RISC OS printer driver for each type of printer, and application programs only need to know how to render to the screen. They have no need to understand the foibles of different printer models.
This bright idea leads us to the current anatomy of printing on RISC OS.
How RISC OS prints
First of all, there is the !Printers application, which is what you see most of the time. This is a huge chunk of Basic (though it actually needs the C compiler preprocessor to build), and it is only a front-end; it provides a user interface to pick the current printer, the destination for output, queue control etc. It also implements a Wimp message protocol between applications and the printing system. This Wimp protocol was not necessary originally and was extended with the introduction of RISC OS 3.
Whilst one might picture the creator of !Printers as being a Basic programmer of no talent, plodding along and making something complex out of something simple, the main part of !Printers was much more like rocket science. Roughly speaking, an application issues a SWI call to say that it would like to print stuff, and RISC OS then captures screen output and sends it to the printer until another SWI call tells it that the application has finished printing.
So, is it all so simple that RISC OS just creates a virtual screen in a sprite the size of a printer's page and renders stuff to it? Well, no; it's not quite that straightforward. Whilst many printers work at a raster level where a large bitmap is ideal, some of the most important ones (e.g. PostScript) are vector devices: they expect commands telling them to draw curves or put characters at certain places on the page.
To tackle this we have to intercept commands to components of RISC OS before they attempt to render them to the screen. This happens in various ways; sometimes the SWI calls are intercepted, and sometimes the modules are smart enough to know that, if printing is going on, they should pass commands on to the printer drivers.
More importantly, we have invented 'classes' of printers. Whilst most bitmap printers can work in one way, forming one class, PostScript printers will work a totally different way and are in another class.
There are a few more complications. Real printers have a much higher density of dots than monitor screens, so a sprite covering an A4 page would use a lot of memory, and RISC OS doesn't have its own virtual memory management system. Rendering, therefore, has to take place in strips. Printers don't have much (or any) brightness resolution (dots are just on or off), so the printer driver has to cope with halftoning: simulating brightness resolution by trading off some spatial resolution.
Consider an application using a font. It opens the font with the screen resolution and paints some text; the printer driver intercepts the call, opens another font with the printer resolution, and uses that to paint the text.
As a result of such fun and games, the component of the printer driver which does this work is massively complex, and wraps itself around many parts of RISC OS. Little surprise that few people have produced new printer drivers, and that the existing ones have taken many years to debug.
However, within classes of printer there are substantial similarities. Most dot-matrix, inkjet or laser printers work on a raster of dots: if we can render to a sprite, then most printers in this class can easily be dealt with by slight variations in the way that the sprite is sent to them.
Coping with different printer models
Going back to the golden days, most printers were 'Epson compatible', which meant that they supported a set of escape sequences for controlling things. One sequence might have switched to graphics mode; another sent a series of dots to be printed.
Although many users look back on these days as happy times, still they ask of printers, scanners and toasters, "is it Epson compatible?" (ha!). The makers of printers obviously didn't like putting their main competitor's name on the box, so every family of printers soon had a different set of control codes, but the idea was the same: one sequence of codes to start a new line and another to enter graphics mode.
To cope with this, the PDF, or Printer Definition File, was invented. The PDF is printer-specific: loaded into !Printers, it tells it about the physical printer and lets it pass on information to the main printing code, such as the escape sequences. The PDF defines things like the various resolution and colour modes supported by the printer, what paper sizes it can use, and so on.
(Note that this definition of PDF has nothing whatever to do with Adobe PDF files. Adobe PDF stands for Portable Document Format, and is a file format based on PostScript that allows documents to be used on many different computer systems. The terminology does get slightly confusing, though, now that RISC OS 4 comes with Taborca, which is a RISC OS printer driver that can output Adobe PDF files.)
A million PDFs could now bloom, and you can find most of them still supplied with your copy of RISC OS. A small application called PrintEdit is supplied to edit the PDFs, and this allows you to edit the escape sequences, add resolutions and so on. PrintEdit is probably a bit overlooked; often, in combination with some guesswork or the manufacturer's command set, it would provide a way of using printers for which there isn't a perfect ready-written driver.
Growing complexity: printer dumpersWe're still talking about bitmap printers. RISC OS generates a bitmap and slices it up, feeding it to the printer using the escape sequence definitions from the PDF. This is the happy situation in the days of RISC OS 2, circa 1990.
The next problem is that the way the bitmap is sliced up starts to be different for different printers. Perhaps some want strips one pixel wide, and others 24; maybe some want to perform halftoning themselves. The options which the PDF can reasonably support are just too limited.
The choice is to make anyone wanting to support a new printer write an entire printer driver (many hundreds of files of Assembler and much worry) or to extend the PDF format (which is already complex enough, and any extension is unlikely to make everyone happy).
I recall being given the printer driver source code by Acorn in around 1990 in order to write a driver for the Star colour dot-matrix printer. It was a difficult job finding my way around it. Any subsequent changes or bug-fixes that Acorn made were not going to percolate into our specific version of the driver.
There had to be a better way... and with RISC OS 3 the dumper module was invented. A dumper sits between the main bitmap printer driver code and the printer. Unlike a PDF, it is not a table of values; it is a RISC OS module: a piece of code which is given a general bitmap of what is to be printed and scope to do as much damage as it wants.
PDFs are retained, and within the class of bitmap printers we can now have sub-classes, one based on an Epson-style dumper, another on a Canon one, and yet another for the HP LaserJet.
Press F12, type *help modules, and you'll see some of these dumper modules lurking in the list of modules. Note that dumper modules only work within the bitmap printer class.
As I've tried to make clear above, the printer drivers and their various interfaces represent a huge investment of effort. However, something substantial has been gained: all programs can print, and printing provides a way of getting output from any program.
Text mode printing
One digression at this point: most of the discussion above is about "graphics printing", representing printout by dots. The old-time printers had built-in character sets, which were what programs like First Word Plus mostly used. To keep these ancient programs and their users happy, Acorn extended !Printers to support 'text mode' or 'fancy text' printing: a scenario where "cut-down version of the Epson control codes meets the First Word Plus file format."
The PDFs again hold printer-specific details for these text modes. On the whole, use of this feature has declined over the years, but it is still there, so whilst I glossily paint the wonders of being able to trap the output of every program using the printer driver system, you may point out that this does not include programs like Edit (text mode printing) or First Word Plus (direct access to the printer port). On the whole, text mode was unnecessary and a bad idea, but Acorn was in the business of keeping its users happy (oh yes!), so it had to be implemented.
Let's say, then, that you have an application which needs to accept the output from any other application as a bitmap; a fax program, for example. How can you make the printer drivers work for you?
The obvious solution to me was to use the ability of !Printers to direct all output to file. That lets us get at the rendered image. We also need some sort of control codes. These can be arbitrary, though; they don't have to be Epson-approved, just something which your program can decode. Some simple codes, then: escape-S for start of page, etc. All this is easily set up with PrintEdit, along with the resolutions of the supported print modes.
So, applications print in the usual way, !Printers sends the output to a file, and you can have a small application program which monitors the printout file and, when it is ready, reads it and converts it back into a bitmap image.
This is exactly how ArcFax works.
Often, ArcFax would be useful just to produce a document as a bitmap for use on the computer, but it is limited to fax resolutions and black and white, so people asked me to do another program that would let them print from any application and get a sprite: they wanted a choice of any resolution, and colour or grey-scale output. I considered doing this using the above technique and maybe basing it on one of the existing colour printer drivers.
The problem with this is that the existing colour drivers deal with colour dots which are either on or off, so although this trick would work, results would not be as useful as they could be. The solution of course was to write a dumper module that gets access to a sprite of what is being printed with 256 levels of brightness.
Now, ideally we want the dumper module to write a sprite file to the printer output. This would let you set !Printers to print to file and just pick up sprites off disc. However, this is not possible because you can't write a sprite without backtracking, and the printer output stream won't allow this. To get around this I produced a dumper module which just fed out the bitmap in a simple coded form, and then had a front-end program that monitored the file and turned it into a bitmap.
This article was originally published in Foundation RISC User