This document is intended to tell application authors how to write their applications so as to make use of the standard RISC OS printer driver interface. It starts with a general explanation of how the printer drivers are supposed to be used. Then the SWI interface is described and an example BASIC procedure is given that shows an application might do its printing. The final section of the document specifies exactly what VDU sequences and other plotting operations the application may use while printing.

PRINTER DRIVER PHILOSOPHY

What the application user sees

RISC OS comes with two printer driver applications, !PSPrinter for PostScript printers and !DMPrinter for a range of dot matrix printers.

If I possess a PostScript printer and I wish to print, I should do the following:

Double-click on !PSPrinter. An icon will appear. Menu on this and check that the settings are right for where the printer is connected, e.g. net/parallel/serial etc. This setting will be retained in CMOS RAM, so it need only be done once.

If I wish to print Text files, drag them onto the printer icon. They will get printed.

If I wish to print from ArcDraw, click Print on the Print dialogue box after running !PSPrinter. The diagram will be printed. Or, drag the icon from the Save dialogue box onto the printer icon.

If I wish to print from ArcPaint, it's exactly the same.

If I with to print from any other editor program, it's exactly the same.

If I have a dot matrix printer, run !DMPrinter. Click on the printer icon to produce a dialogue box of the correct type of printer. Click on the printer-type field to cycle through the various dot matrix printers supported, until it reflects mine. If I can't find mine, seek help.

Drivers for other printers will appear, from Acorn and from others, in an analogous form. New printer drivers will work with all existing applications, subject to the limitations of any particular printing device.

How and Why

I use the term "editor" to mean any sort of interactive program that prepares, changes and processes data files. This includes word processors, DTP packages, spreadsheets, draw programs, etc. I use the term "Foo" to refer to any editor program, which operates on "Foo data files": so, substitute ArcDraw, ArcPaint, or whatever as you read. I use the term "Poo" to refer to any printer capable of generating graphics, e.g. FX80, Apple LaserWriter, etc.

In the general case, editors save data in files of their own format, with an appropriate file type. Only Foo understands Foo data files. Also, different printers are entirely different from each other, and a pain to drive. If each editor is to understand each printer, and there are X editors and Y printers in the world, then:

(a) There is X*Y work to do
(b) If a new printer type comes along, all applications need updating.

Because of this, RISC OS separates printer drivers from editors so that they are separate programs. This means:

(a) There is X+Y work to do
(b) If a new printer type comes along, once a printer driver has been

      written for it, it will work with all applications.

This is clearly a better situation, and is one of the main reasons why applications working together is important.

How do we achieve this? The main step is to define an interface that all editors should use when talking to printers, a sort of "virtual printer interface". All editors then output to this virtual interface, knowing little of the characteristics of the physical printer itself.

The "virtual printer interface" chosen in RISC OS is to say that the printer supports a specified subset of the screen graphics primitives. This is because a WYSIWYG editor (where what you see on the screen is what you get on paper) will already contain code to render the data file using screen primitives; provided the application restricts itself to the specified subset, printing is unlikely to require very much extra code.

There are several reasons why not all the screen primitives are supported by the virtual printer interface. The main ones are:

(a) Some screen primitives are screen hardware specific (e.g. flashing

      colours, hardware palette changes, etc.).
(b) Some screen primitives are very hard or impossible to implement on
      common types of printer (e.g. PostScript printers cannot handle
      non-overwriting GCOL actions).
(c) Some screen primitives - e.g. flood fill - cannot be split across
      multiple boxes and so do not work with the window system. The printer
      drivers use a similar interface to the window system and have the same
      problems with these calls as it does.

Each printer driver is implemented as a RISC OS relocatable module, which provides extra SWIs (system calls) concerned with starting, stopping and controlling a print job. The interface allows the printer driver to ask the application to render specific rectangles of the picture in any order, to give the printer driver maximum freedom in manipulating buffer space, etc.

The interface to printer drivers means that an editor is not able to supply an acceptable user interface for setting printer-specific options such as quality/draft mode. For this reason, a separate application exists for each possible printer driver, to allow any printer-specific options to be set.

The printing of plain text is such a basic facility that it is considered unacceptable to require the loading of ArcEdit. Furthermore, printers tend to provide a much simpler method for the printing of simple text than the facilities provided for arbitrary graphics. For this reason, every printer interface program provides simple facilities for the printing of plain text files.

THE SWI INTERFACE

To send output to the printer, an application must engage in a dialogue with the printer driver. Parts of this dialogue is similar to the dialogue with the window manager when a window requires redrawing. The dialogue can be summarised as follows:

The application starts by opening a file to receive the printer driver's output. Typically this file is "printer:", but any file may be used. It passes this file's handle to SWI PDriver_SelectJob in order to start a print job.

To output a page, the application then uses SWI PDriver_GiveRectangle to give the printer driver a list of the rectangles it wants to appear on the page and where it wants them to appear.

When it has specified all the rectangles it wants printed, it uses SWI PDriver_DrawPage to ask the printer driver for the first rectangle to be printed. It then prints this rectangle and uses SWI PDriver_GetRectangle to get another rectangle to print, until there are no further rectangles to print. This is very similar to the way an application uses SWI Wimp_RedrawWindow and SWI Wimp_GetRectangle when redrawing its window.

When all the desired pages have been output in this manner, the application first uses SWI PDriver_EndJob to end the print job, then closes the output file.

The full set of printer driver SWIs is as follows:

SWI PDriver_Info (&80140)


Entry: -

Exit: R0 (top 16 bits) identifies general type of printer driven.

            Current assignments:
              0 = PostScript printers.
              1 = FX80 and similar dot matrix printers.
         R0 (bottom 16 bits) = 100 * version number of printer driver.
         R1 = X pixel resolution of printer driven (pixels/inch).
         R2 = Y pixel resolution of printer driven (pixels/inch).
         R3 = features word:
              Bit 0 set => colour
              If bit 0 set, bit 1 set => full colour range not available.
              Bit 2 set => only a discrete set of colours supported.
              Bit 8 set => cannot handle filled shapes well.
              Bit 9 set => cannot handle thick lines well.
              Bit 10 set => cannot handle overwriting well.
              Bit 24 set => supports the PDriver_ScreenDump call.
              Bit 25 set => supports arbitrary transformations (else only
                            axis-preserving ones).
         R4 -> adjectival description of printers supported (a maximum of 20
              characters, excluding the zero-termination).
         R5 = X halftone resolution (repeats/inch). If no halftoning is done,
              this is equal to the value returned in R1.
         R6 = Y halftone resolution (repeats/inch). If no halftoning is done,
              this is equal to the value returned in R2.
         R7 identifies a specific configured printer. Assignments of these
              numbers are specific to each printer driver.

Some of these values can be changed by the configuration application associated with the printer driver (using SWI PDriver_SetInfo). If SWI PDriver_Info is called while a print job is selected, the values returned are those that were in effect when that print job was started (i.e. when it was first selected using PDriver_SelectJob). If SWI PDriver_Info is called when no print job is active, the values returned are those that would be used for a new print job.

The printer/driver features word returned in R3 describes the various ways in which the printers supported differ from a "standard" printer. Most applications need not look at this word, but sophisticated ones may want to in order to ensure that the printer can handle their output, or to adjust their output to the printer being used.

In more detail than above, the bits in R3 have the following meanings:

Bits 0-7 relate to the printer/driver's colour capabilities:

Bit 0 = 0: It can only print grey colours. (I.e. it produces monochrome

            output.)
Bit 0 = 1: It supports at least some non-grey colours.

Bit 1 is irrelevant if bit 0 = 0. Otherwise: Bit 1 = 0: It supports the full colour range - i.e. it can manage each of

            the eight primary colours (white, yellow, cyan, green, magenta,
            red, blue, black).
Bit 1 = 1: It supports only a limited set of colours (this would typically
            be set e.g. for an XY-plotter which only had black, red, blue and
            green pens loaded).

Bit 2 = 0: It supports a semi-continuous range of colours at the software

            level (e.g. if bit 0 = 0 and bit 2 = 0, the application can
            expect to be able to plot a reasonable approximation to any
            grey).
Bit 2 = 1: It only supports a discrete set of colours at the software level Bits 3-7 are reserved - current printer drivers will set them to zero.

Bits 8-15 relate to the printer/driver's plotting capabilities:

Bit 8 = 0: It can handle filled shapes. Bit 8 = 1: It cannot handle filled shapes other than by outlining them (e.g.

            an unsophisticated XY-plotter driver would have this bit set).

Bit 9 = 0: It can handle thick lines.
Bit 9 = 1: It cannot handle thick lines other than by plotting a thin line.

            (Unsophisticated XY-plotter drivers might again come into this
            category. It differs from the filled shape criterion in bit 8 in
            that it can be solved, at least partially, if the plotter has a
            range of pens of differing thicknesses available.)

Bit 10 = 0: It handles overwriting of one colour by another on the paper

            properly (this is generally true of any printer/driver that
            buffers its output, either in the printer or in the driver).
Bit 10 = 1: It does not handle overwriting of one colour by another properly Bits 11-15 are reserved - current printer drivers will set them to zero.

Bits 16-23 are reserved - current printer drivers will set them to zero.

Bits 24-31 relate to optional features of the printer driver:

Bit 24 = 0: This printer driver does not support screen dumps. Bit 24 = 1: This printer driver does support screen dumps.

Bit 25 = 0: This printer driver does not support transformations (supplied

            to PDriver_DrawPage) other than scalings, translations, rotations
            by multiples of 90 degrees and combinations thereof.
Bit 25 = 1: This printer driver supports arbitrary transformations supplied
            to PDriver_DrawPage.

Bits 25-31 are reserved - current printer drivers will set them to zero.

SWI PDriver_SetInfo (&80141)


Entry: R1 = X pixel resolution of printer driven (pixels/inch).

         R2 = Y pixel resolution of printer driven (pixels/inch).
         R3 (bit 0) is zero to set monochrome, 1 to set colour. The other
              bits of R3 are ignored.
         R5 = X halftone resolution (repeats/inch). If no halftoning is to be
              done, this should be equal to the value in R1.
         R6 = Y halftone resolution (repeats/inch). If no halftoning is to be
              done, this should be equal to the value in R2.
         R7 identifies a specific configured printer.

Exit: -

The configuration application associated with a particular printer driver uses this SWI to change the information values associated with subsequent print jobs. In general, no other application should use this SWI.

SWI PDriver_CheckFeatures (&80142)


Entry: R0 = features word mask.

         R1 = features word value.

Exit: If the features word that PDriver_Info would return in R3 satisfies

         ((features word) AND R0) = (R1 AND R0), return is normal with all
         registers preserved. Otherwise a suitable error is generated, if
         appropriate - e.g. no error will be generated if the printer driver
         has the ability to support arbitrary rotations and your features
         word value merely requests axis-preserving ones.

This call may be used by an application to check that the current printer driver has the features it requires, and to generate a suitable descriptive error if it doesn't.

SWI PDriver_PageSize (&80143)


Entry: -

Exit: R1 = X size of paper, including margins. (Units 1/72000 inch)

         R2 = Y size of paper, including margins. (Units 1/72000 inch)
         R3 = left edge of printable area of paper, relative to the left edge
              of the paper. (Units 1/72000 inch)
         R4 = bottom edge of printable area of paper, relative to the bottom
              edge of the paper. (Units 1/72000 inch)
         R5 = right edge of printable area of paper, relative to the left
              edge of the paper. (Units 1/72000 inch)
         R6 = top edge of printable area of paper, relative to the bottom
              edge of the paper. (Units 1/72000 inch)

An application can use this call to find out how big the paper in use is and how large the printable area on the paper is. This information can then be used to decide how to place the data to be printed on the page.

These values can be changed by the configuration application associated with the printer driver (using SWI PDriver_SetPageSize). If SWI PDriver_PageSize is called while a print job is selected, the values returned are those that were in effect when that print job was started (i.e. when it was first selected using PDriver_SelectJob). If SWI PDriver_PageSize is called when no print job is active, the values returned are those that would be used for a new print job.

SWI PDriver_SetPageSize (&80144)


Entry: R1 = X size of paper, including margins. (Units 1/72000 inch)

         R2 = Y size of paper, including margins. (Units 1/72000 inch)
         R3 = left edge of printable area of paper, relative to the left edge
              of the paper. (Units 1/72000 inch)
         R4 = bottom edge of printable area of paper, relative to the bottom
              edge of the paper. (Units 1/72000 inch)
         R5 = right edge of printable area of paper, relative to the left
              edge of the paper. (Units 1/72000 inch)
         R6 = top edge of printable area of paper, relative to the bottom
              edge of the paper. (Units 1/72000 inch)

Exit: -

The configuration application associated with a particular printer driver uses this SWI to change the page size values associated with subsequent print jobs. In general, no other application should use this SWI.

SWI PDriver_SelectJob (&80145)


Entry: R0 = file handle for print job to be selected, or zero to cease

              having any print job selected.
         R1 is zero or points to a title string for the job. This title
              string is terminated by any character outside the range ASCII
              32-126.

Exit: R0 = file handle for print job that was previously active, or zero

              if no print job was active.

A print job is identified by a file handle, which must be that of a file that is open for output. The printer output for the job concerned is sent to this file.

Calling SWI PDriver_SelectJob with R0=0 will cause the current print job (if any) to be suspended, and the printer driver will cease intercepting plotting calls.

Calling SWI PDriver_SelectJob with R0 containing a file handle will cause the current print job (if any) to be suspended, and a print job with the given file handle to be selected. If a print job with this file handle already exists, it is resumed; otherwise a new print job is started. The printer driver will start to intercept plotting calls if it is not already doing so.

Note that this call never ends a print job - to do so, use one of SWI PDriver_EndJob and SWI PDriver_AbortJob.

The title string pointed to by R1 is treated by different printer drivers in different ways. It is only ever used if a new print job is being started, not when an old one is being resumed. Typical uses are:

(a) A simple printer driver might ignore it. (b) The PostScript printer driver adds a line "%%Title: " followed by the

      given title string to the PostScript header it generates.
(c) Printer drivers whose output is destined for an expensive central
      printer in a large organisation might use it when generating a cover
      sheet for the document.

An application is always entitled not to supply a title (by setting R1=0), and a printer driver is entitled to ignore any title supplied.

Printer drivers may also use the following OS variables when creating cover sheets, etc.:

  PDriver$For     - indicates who the output is intended to go to.
PDriver$Address - indicates where to send the output.

These variables should not contain characters outside the range ASCII 32-126.

If an error occurs during PDriver_SelectJob, the previous job will still be selected afterwards (though it may have been de-selected and re-selected during the call). No new job will exist (one may have been created during the call, but the error will cause it to be destroyed again).

SWI PDriver_CurrentJob (&80146)


Entry: -

Exit: R0 = file handle for print job that is currently active, or zero if

              no print job is active.

SWI PDriver_FontSWI (&80147)


This SWI is part of the internal interface between the font system and printer drivers. Applications should not call it.

SWI PDriver_EndJob (&80148)


Entry: R0 = file handle for print job to be ended.

Exit: -

This SWI should be used to end a print job normally. This may result in further printer output - e.g. the PostScript printer driver will produce the standard trailer comments.

If the print job being ended is the currently active one, there will be no current print job after this call (so plotting calls will no longer be intercepted).

If the print job being ended is not currently active, it will be ended without altering which print job is currently active or whether plotting calls are being intercepted.

SWI PDriver_AbortJob (&80149)


Entry: R0 = file handle for print job to be aborted.

Exit: -

This SWI should be used to end a print job abnormally - it should generally be called after errors while printing. It will not try to produce any further printer output - this is important if an error occurs while sending output to the print job's output file.

If the print job being aborted is the currently active one, there will be no current print job after this call (so plotting calls will no longer be intercepted).

If the print job being aborted is not currently active, it will be aborted without altering which print job is currently active or whether plotting calls are being intercepted.

Application authors should note (a) that many error messages reported by the printer driver are buffered within a print job's workspace; (b) that aborting the print job with this call (or with PDriver_Reset) releases the print job's workspace back to the heap. So if (as is likely) the application wants to report the error after issuing SWI PDriver_AbortJob, it should take a copy of the error number and message first. (This is even more important if the application is going to close the output file before reporting the error, as filing systems are quite liable to claim some workspace from the heap temporarily while closing the file.)

SWI PDriver_Reset (&8014A)


Entry: -

Exit: -

This SWI aborts all print jobs known to the printer driver, leaving the printer driver with no active or suspended print jobs and ensuring that plotting calls are not being intercepted.

Normal applications shouldn't use this SWI, but it can be useful as an emergency recovery measure when developing an application.

A call to this SWI is generated automatically if the machine is reset or the printer driver module is killed or RMTIDYed.

SWI PDriver_GiveRectangle (&8014B)


Entry: R0 = rectangle identification word. This word is reported back to

              the application when it is requested to plot all or part of
              this rectangle.
         R1 -> 4 word block, containing rectangle to be plotted. Units are OS
              units.
         R2 -> 4 word block, containing dimensionless transformation to be
              applied to the specified rectangle before printing it. The
              entries are given as fixed point numbers with 16 binary places,
              so the transformation is:
                x' = (x * R2!0 + y * R2!8)/2^16
                y' = (x * R2!4 + y * R2!12)/2^16
         R3 -> 2 word block, containing the position where the bottom left
              corner of the rectangle is to be plotted on the printed page.
              Units are 1/72000 inch.
         R4 = background colour for this rectangle, in the form &BBGGRRXX.

Exit: -

This SWI allows an application to specify a rectangle from its workspace to be printed, how it is to be transformed and where it is to appear on the printed page. An application should make one or more calls to SWI PDriver_GiveRectangle before a call to SWI PDriver_DrawPage and the subsequent calls to SWI PDriver_GetRectangle. (Multiple calls allow the application to print multiple rectangles from its workspace to one printed page - e.g. for "two up" printing).

The printer driver may subsequently ask the application to plot the specified rectangles or parts thereof in any order it chooses. An application should not make any assumptions about this order or whether the rectangles it specifies will be split. (A common reason why a printer driver might split a rectangle is that it prints the page in strips to avoid using excessively large page buffers.)

Assuming that a non-zero number of copies is requested and that none of the requested rectangles go outside the area available for printing, it is certain to ask the application to plot everything requested at least once. It may ask for some areas to be plotted more than once, even if only one copy is being printed, and it may ask for areas marginally outside the requested rectangles to be plotted (this can typically happen if the boundaries of the requested rectangles are not on exact device pixel boundaries).

If SWI PDriver_GiveRectangle is used to specify a set of rectangles that overlap on the output page, the rectangles will be printed in the order of the SWI PDriver_GiveRectangle calls. For appropriate printers (i.e. most printers, but not e.g. XY plotters), this means that rectangles supplied via later PDriver_GiveRectangle calls will overwrite rectangles supplied via earlier calls.

SWI PDriver_DrawPage (&8014C)


Entry: R0 = number of copies to print.

         R1 -> 4 word block, to receive the rectangle to print.
         R2 is zero or contains the page's sequence number within the
              document being printed (i.e. 1-n for an n-page document).
         R3 is zero or points to a string, terminated by a character in the
              ASCII range 33-126 (note spaces are not allowed), which gives
              the 'real' page number. (Examples: "23", "viii", "A-1")

Exit: R0 = number of copies still requiring printing. This is zero if and

              only if no more plotting is required.
         If R0 is non-zero, the area pointed to by R1 has been filled in with
              the rectangle that needs to be plotted, and R2 contains the
              rectangle identification word for the user-specified rectangle
              that this is a part of.
         If R0 is zero, the contents of R2 and the area pointed to by R1 are
              undefined.

This SWI should be called after all rectangles to be plotted on the current page have been specified (using SWI PDriver_GiveRectangle). It returns the first rectangle that the printer driver wants plotted in the area pointed to by R1. If nothing requires plotting - i.e. if there is no such rectangle - it returns R0=0.

So the application should stop trying to plot the current page if R0=0, and continue otherwise. If R0<>0, the fact that R0 is the number of copies still to be printed is only intended to be used for information purposes (e.g. putting a "Printing page m of n" message on the screen); as long as it is non-zero, R0's value does not affect what the application should try to plot.

The information passed in R2 and R3 is not particularly important, though it helps to make output produced by the PostScript printer driver conform better to Adobe's structuring conventions. If non-zero values are supplied, they should be correct. Note that R2 is NOT the sequence number of the page in the print job, but in the document.

An example: if a document consists of 11 pages, numbered "" (the title page), "i"-"iii" and "1"-"7", and the application is requested to print the entire preface part, it should use R2 = 2, 3, 4 and R3 -> "i", "ii", "iii" for the three pages.

When plotting starts in a rectangle supplied by a printer driver, the printer driver behaves as though the VDU system is in the following state:

This is designed to be as similar as possible to the state set up by the window manager when redrawing.

SWI PDriver_GetRectangle (&8014D)


Entry: R1 -> 4 word block, to receive the rectangle to print.

Exit: R0 = number of copies still requiring printing. This is zero if and

              only if no more plotting is required.
         If R0 is non-zero, the area pointed to by R1 has been filled in with
              the rectangle that needs to be plotted, and R2 contains the
              rectangle identification word for the user-specified rectangle
              that this is a part of.
         If R0 is zero, the contents of R2 and the area pointed to by R1 are
              undefined.

This SWI should be used after plotting a rectangle returned by a previous call to SWI PDriver_DrawPage or SWI PDriver_GetRectangle, to get the next rectangle the printer driver wants plotted. It returns precisely the same information as PDriver_DrawPage.

SWI PDriver_CancelJob (&8014E)


Entry: R0 = file handle for job to be cancelled.

Exit: -

This SWI causes subsequent attempts to output to the print job associated with the given file handle to do nothing other than generate the error "Print cancelled". An application is expected to respond to this error by aborting the print job.

SWI PDriver_ScreenDump (&8014F)


Entry: R0 = file handle of file to receive the dump.

Exit: -

If this SWI is supported (i.e. if bit 24 is set in the value SWI PDriver_Info returns in R3), this SWI causes the printer driver to output a screen dump to the file handle supplied in R0. The file concerned should be open for output. If the SWI is not supported, an error is returned.

SWI PDriver_EnumerateJobs (&80150)


Entry: R0 = 0 to get file handle of first print job, or file handle of a

              print job to get the file handle of the next print job.
Exit: R0 = file handle of print job requested, or 0 if there are no more
              print jobs.

This allows the print jobs that exist to be enumerated. The order in which they appear is undefined.

SWI PDriver_SetPrinter (&80151)


Entry: Printer-driver specific.
Exit: Printer-driver specific.

This allows the setting of options specific to a particular printer driver. See the individual printer driver documentation for details. In general, this SWI is used by the configuration application associated with the printer driver module and no other application should use it.

EXAMPLE BASIC PROCEDURE

An example BASIC procedure that does a standard "two up" printing job:

DEFPROCprintout(firstpage%, lastpage%, title$, filename$) :
REM Get SWI numbers used in this procedure. LOCAL select%, abort%, pagesize%, giverect%, drawpage%, getrect%, end% SYS "OS_SWINumberFromString",,"PDriver_SelectJob" TO select% SYS "OS_SWINumberFromString",,"PDriver_AbortJob" TO abort% SYS "OS_SWINumberFromString",,"PDriver_PageSize" TO pagesize% SYS "OS_SWINumberFromString",,"PDriver_GiveRectangle" TO giverect% SYS "OS_SWINumberFromString",,"PDriver_DrawPage" TO drawpage% SYS "OS_SWINumberFromString",,"PDriver_GetRectangle" TO getrect% SYS "OS_SWINumberFromString",,"PDriver_EndJob" TO end% :
REM Open destination file and set up a local error handler that will REM close it again on an error. Note that our local error handlers REM take copies of error messages and numbers: this avoids the danger REM that they will be lost when a print job is aborted or a subsequent REM file operation occurs.
LOCAL H%, O%
H% = OPENOUT(filename$)
LOCAL e%, e$
LOCAL ERROR
ON ERROR LOCAL:RESTORE ERROR:PROCrderr:CLOSE#H%:PROCerr :
REM Start up a print job associated with this file, remembering the REM handle associated with the previous print job (if any), then set up a REM a local error handler for it.
SYS select%,H%,title$ TO O%
LOCAL ERROR
ON ERROR LOCAL:RESTORE ERROR:PROCrderr:SYSabort%,H%:SYSselect%,O%:PROCerr :
REM Now we decide how two pages are to fit on a piece of paper. LOCAL left%, bottom%, right%, top%
PROCgetdocumentsize(box%)
SYS pagesize% TO ,,,left%,bottom%,right%,top% PROCfittwopages(left%,bottom%,right%,top%,box%,matrix%,origin1%,origin2%) :
REM Start the double page loop
LOCAL page%, copiesleft%, pagetoprint%, white% white%=&FFFFFF00
FOR page%=firstpage% TO lastpage% STEP 2

      :
      REM Set up to print two pages, or possibly just one if an odd number of
      REM pages are being printed and this is the last time around.
      SYS giverect%, page%, box%, matrix%, origin1%, white%
      IF page%<lastpage% THEN
        SYS giverect%, page%+1, box%, matrix%, origin2%, white%
      ENDIF
      :
      REM Start printing. As each printed page corresponds to two document
      REM pages, we cannot easily assign any sensible page numbers to printed
      REM pages. So we simply pass zeroes to PDriver_DrawPage.
      SYS drawpage%,1,box2%,0,0 TO copiesleft%,,pagetoprint%
      WHILE copiesleft%<>0
        PROCprintpage(pagetoprint%, box2%)
        SYS getrect%,,box% TO copiesleft%,,pagetoprint%
      ENDWHILE
      :
REM End of page loop
NEXT
:
REM All pages have now been printed. Terminate this print job. SYS end%,H%
:
REM Go back to the first of our local error handlers. RESTORE ERROR
:
REM And go back to whatever print job was active on entry to this REM procedure (or to no print job in no print job was active). SYS select%,O%
:
REM Go back to the caller's error handler. RESTORE ERROR
:
REM Close the destination file.
CLOSE#H%
ENDPROC
:
REM Procedure to take a copy of the current error report DEFPROCrderr
e%=ERR: e$=REPORT$
ENDPROC
:
REM Procedure to re-report the error last registered with PROCrderr, with REM a backtrace error line attached to it. DEFPROCerr
ERROR e%,e$+" (from line "+STR$(ERL)+")" ENDPROC

This uses the following global areas of store:

  box%:     4 words
box2%: 4 words
matrix%: 4 words
origin1%: 2 words
origin2%: 2 words

And the following external procedures:

DEFPROCgetdocumentsize(box%)

DEFPROCfittwopages(l%, b%, r%, t%, box%, transform%, org1%, org2%) DEFPROCdrawpage(page%, box%)
If printing is likely to take a long time and the application does not want to hold other applications up while it prints, it should regularly use a sequence like the following during printing:

SYS select%,O%
SYS "Wimp_Poll",mask%,area% TO reason% <process reason% as appropriate> SYS select%,H% TO O%

THE VIRTUAL PRINTER INTERFACE

When a print job is active, the printer driver intercepts the following vectors:

(a) WrchV (= WriteCV): all character output is received and interpreted by

      the printer driver. Many VDU sequences are converted into printer
      output to do similar things on the printed page. Some VDU sequences
      produce errors. A few are passed through to the real VDU drivers. Only
      these last go through the usual output stream selection controlled by
      OS_Byte 3.
        See the section entitled 'VDU SEQUENCES' below for more details.

(b) SpriteV: most sprite operations are passed on unchanged to the

      SpriteExtend module and/or the operating system. Those that do sprite
      plotting are intercepted and generate printer output to do similar
      sprite plotting on the printed page.
        Note that, because of the way vector interception is done, the
      SpriteExtend module must be initialised before the printer driver.
        See the section entitled 'SPRITE OPERATIONS' below for more details.

(c) DrawV: Draw calls that would not normally plot to the screen are passed

      on unchanged to the Draw module. Others are intercepted and, where
      possible, used to generate printer output to do similar things on the
      printed page. However, some of the intercepted calls cannot be so
      treated and instead produce errors.
        Note that, because of the way vector interception is done, the
      Draw module must be initialised before the printer driver.
        See the section entitled 'DRAW MODULE CALLS' below for more details.

(d) ColourV: some ColourTrans calls are intercepted and processed by the

      printer driver, others are left to be dealt with by the ColourTrans
      module itself.
        Note that, because of the way vector interception is done, the
      ColourTrans module must be initialised before the printer driver.
        See the section entitled 'COLOURTRANS MODULE CALLS' below for more
      details.

(e) ByteV: The OS_Byte calls dealing with dot-dash repeat lengths (OS_Byte

      163,242,0-64) are intercepted and processed by the printer driver. The
      same applies to OS_Byte 218 (read/write bytes in VDU queue).
        See the section entitled 'MISCELLANEOUS CALLS' below for more
      details.

In addition, the font manager and the printer driver interact to cause many of the font manager calls to be processed by the printer driver. See the section entitled 'FONT MANAGER CALLS' below for more details.

VDU SEQUENCES

General rules

Whenever a print job is active, the printer driver will intercept all characters sent through WrchV. It will then queue them in the same way as the VDU drivers do and process complete VDU sequences as they appear. Because the printer driver will not pick up any data currently in the VDU queue, and may send sequences of its own to the VDU drivers, a print job should not be selected with an incomplete sequence in the VDU queue.

Also, because the printer driver may send sequences of its own to the VDU drivers, the output stream specification set by OS_Byte 3 should be in its standard state (i.e. as though set by OS_Byte 3,0).

The printer driver will pass the following VDU sequences through to the normal VDU drivers, either because they control the screen hardware or because they affect global resources such as the character and ECF definitions:

  VDU 7                         - Produce bell sound
  VDU 19,l,p,r,g,b              - Change hardware palette
  VDU 20                        - Set default hardware palette
  VDU 23,0,n,m|                 - "Program 6845 registers"
  VDU 23,1,n|                   - Change cursor appearance
VDU 23,2-5,a,b,c,d,e,f,g,h - Set ECF pattern
  VDU 23,9-10,n|                - Set flash durations
  VDU 23,11|                    - Set default ECF patterns
VDU 23,12-15,a,b,c,d,e,f,g,h - Simple setting of ECF pattern
  VDU 23,17,4,m|                - Set ECF type
  VDU 23,17,6,x;y;|             - Set ECF origin
VDU 23,32-255,a,b,c,d,e,f,g,h - Define character

The printer driver will interpret or fault all other VDU sequences. If the printer driver currently wants a rectangle printed (i.e. if there has been a call to SWI PDriver_DrawPage or SWI PDriver_GetRectangle and the last such call returned R0 <> 0), these will result in it producing appropriate output or errors. Otherwise, the printer driver will keep track of some state information (e.g. what the current foreground and background colours are), but will not produce any printer output.

The printer driver will always behave as though it is in VDU 5 state. No text co-ordinate system is defined, and no scrolling is possible. For these reasons, the following VDU sequences are faulted:

   VDU 4                        - exit VDU 5 state
   VDU 23,7,m,d,z|              - scroll display
VDU 23,8,t1,t2,x1,y1,x2,y2| - clear text block

It is generally meaningless to try to send or echo characters directly to the printer while printing. Furthermore, attempts to do so are likely to disrupt the operation of printer drivers. For these reasons, the following VDU sequences are faulted:

   VDU 1,c                      - send character to printer
   VDU 2                        - start echoing characters to printer

It is not possible to change the "mode" of a printed page, so the following VDU sequence is faulted:

   VDU 22,m                     - change display mode

A printer driver cannot be written to deal with undefined or reserved calls, so the following VDU sequences are faulted:

   VDU 23,18-24,...             - reserved for Acorn expansion
   VDU 23,28-31,...             - reserved for use by applications
   VDU 25,216-231,...           - reserved for Acorn expansion
   VDU 25,240-255,...           - reserved for use by applications

The following VDU sequences are ignored, either because they normally do nothing (at least when stuck in VDU 5 mode and not echoing characters to the printer) or because they have no sensible interpretation when applied to printed output rather than a screen.

   VDU 0                        - do nothing
   VDU 3                        - stop echoing characters to printer
   VDU 5                        - enter VDU 5 state
   VDU 14                       - start "paged" display
   VDU 15                       - end "paged" display
   VDU 17,c                     - define text colour
   VDU 23,17,5|                 - exchange text foreground and background
   VDU 27                       - do nothing
   VDU 28,l,b,r,t               - define text window

Colours

Colours are a rather complicated matter. It is strongly recommended that applications should use SWI ColourTrans_SetGCOL, SWI ColourTrans_SelectTable and SWI ColourTrans_SetFontColours to set colours, as these will allow the printer to produce as accurate an approximation as it can to the desired colour, independently of the screen palette. The GCOL sequence (VDU 18,k,c) should only be used if absolutely necessary, and the application writer should be aware of the fact that the printer driver has a simplified interpretation of the parameters, as follows:

The major problems with the use of VDU 18,k,c to set colours are (a) that it makes the printer driver output dependent on the current screen mode and palette; (b) that it artificially limits the printer driver to the number of colours displayed on the screen (which can be very limiting in a two colour mode!).

Other techniques that depend on GCOLs (e.g. SWI Font_SetFontColours, colour-changing sequences in strings passed to Font_Paint, plotting sprites without a translation table, etc.) have the same problems and are similarly not recommended.

No operations other than overwriting are permitted, mainly because they cannot be implemented on many common printers (e.g. PostScript printers).

Note that the printer driver maintains its own foreground and background colour information. The screen foreground and background colours are not affected by VDU 18,k,c sequences encountered while a print job is active.

Similarly, VDU 23,17,2-3,t| sequences encountered while a print job is active do not affect the screen tints, just the printer driver's own tints. VDU 23,17,0-1,t| sequences would only affect the colours of the text tints, so the printer driver ignores them.

Other graphics state operations

The VDU 6 and VDU 21 sequences have their normal effects of enabling and disabling execution (but not parsing) of subsequent VDU sequences. As usual, the printer driver keeps track of this independently of the VDU drivers.

The cursor movement VDU sequences (i.e. VDU 8-11, VDU 13, VDU 30 and VDU 31,x,y) all update the current graphics position (without updating the previous graphics positions used in e.g. triangle plotting), precisely as they do in VDU 5 mode on the screen.

VDU 24,l;b;r;t; will set the printer driver's graphics clipping box. The rectangle specified should lie completely within the box that was reported on return from the last call to SWI PDriver_DrawPage or SWI PDriver_GetRectangle. If this is not the case, it is not defined what will happen, and different printer drivers may treat it in different ways. This is analogous to the situation with the window manager: attempts to set a graphics clipping box outside the rectangle currently being redrawn may be ignored completely (if they go outside the screen) or may get obeyed (with consequences that are almost certainly wrong!).

VDU 29,x;y; sets the printer driver's graphics origin.

VDU 26 will reset the printer driver's graphics clipping box to its maximum size (this is essentially the box reported on return from the last call to SWI PDriver_DrawPage or SWI PDriver_GetRectangle, but may be slightly different due to rounding problems when converting from a box expressed in printer pixels to one expressed in OS units). It also resets its versions of the graphics origin, the current graphics position and all the previous graphics positions to (0,0).

VDU 23,6,a,b,c,d,e,f,g,h will set the printer driver's current dot pattern (for use with lines plotted via VDU 25,16-31,x;y; and VDU 25,48-63,x;y;). The exact lengths of the dashes and gaps produced may differ between various printer drivers, and also between the screen and printer drivers, so an application should not rely on them. However, an application can reasonably expect each set bit in the pattern to correspond to approximately 2 to 3 OS units on the printed page.

VDU 23,16,x,y| changes the printer driver's version of the cursor control flags, and thus how the cursor movement control sequences and BBC-style character plotting affect the current graphics position. As usual, this is completely independent of the corresponding flags in the VDU drivers. However, printer drivers pay no attention to the setting of bit 6 (which controls whether movements beyond the edge of the graphics window cause carriage return/line feeds and other cursor movements to be generated automatically) - they always behave as though it is set. Note that the Wimp normally sets this bit, and that it is not sensible to have it clear at any time during a Wimp redraw.

VDU 23,17,7,flags,x;y;| changes the printer driver's version of the size that BBC-style characters are to be plotted and the spacing that is required between them. Setting the VDU 4 character size cannot possibly affect the printer driver's output and so will be ignored completely. As noted below under 'Plotting operations', a "pixel" is regarded as the size of a screen pixel for the screen mode that was in effect when the print job was started.

Plotting operations

The printer driver regards a "pixel" as having size 2 OS units square (1/90 inch square). The main effect of this is that all PLOT line, PLOT point and PLOT outline calls will produce lines that are approximately 2 OS units wide.

However, when translating the character size and spacing information provided by VDU 23,17,7,... (see above) from pixels to OS units, the screen pixel size for the screen mode that was in effect when the print job was started is used. This is done in the expectation that the application is basing its requested sizes on that screen mode.

The following VDU sequences perform straightforward plotting operations; printer drivers will produce the corresponding printed output:

   VDU 12                       - clear graphics window (in VDU 5 state)
   VDU 16                       - clear graphics window
   VDU 25,0-63,x;y;             - draw line; however, the lines are always
                                    plotted solid, so only VDU 25,0-15,...
                                    and VDU 25,32-47,... will look the same
                                    as in VDU output. Use Draw_Stroke to
                                    generate dashed lines that will come
                                    out well in printed output.
   VDU 25,64-71,x;y;            - draw point
   VDU 25,80-87,x;y;            - fill triangle
   VDU 25,96-103,x;y;           - fill axis-aligned rectangle
   VDU 25,112-119,x;y;          - fill parallelogram
   VDU 25,144-151,x;y;          - draw circle
   VDU 25,152-159,x;y;          - fill circle
   VDU 25,160-167,x;y;          - draw circular arc
   VDU 25,168-175,x;y;          - fill circular segment
   VDU 25,176-183,x;y;          - fill circular sector
   VDU 25,192-199,x;y;          - draw ellipse
   VDU 25,200-207,x;y;          - fill ellipse
   VDU 32-126                   - print characters in BBC-style font
   VDU 127                      - backspace & delete
   VDU 128-255                  - print characters in BBC-style font

One difference to note is that most printer drivers will either not do the rounding to pixel centres normally done by the VDU drivers, or will round to different pixel centres (probably the centres of their device pixels).

The following VDU sequences are faulted because they cannot be split up easily across rectangles, and also because they depend on the current picture contents and so cannot be implemented e.g. on PostScript printers:

   VDU 25,72-79,x;y;            - horizontal line fill (flood fill primitive)
   VDU 25,88-95,x;y;            - horizontal line fill (flood fill primitive)
   VDU 25,104-111,x;y;          - horizontal line fill (flood fill primitive)
   VDU 25,120-127,x;y;          - horizontal line fill (flood fill primitive)
   VDU 25,128-143,x;y;          - flood fills
   VDU 25,184-191,x;y;          - copy/move rectangle
Exception: VDU 25,184,x;y; and VDU 25,188,x;y; are now correctly interpreted
by printer drivers (as being equivalent to VDU 25,0,x;y; and VDU 25,4,x;y; respectively).

The sprite plotting VDU sequences (VDU 23,27,m,n| and VDU 25,232-239,x;y;) and the font manager VDU sequences (VDU 23,25,a,b,c,d,e,f,g,h, VDU 23,26,a,b,c,d,e,f,g,h,text and VDU 25,208-215,x;y;text) cannot be handled by the printer drivers and generate errors. Application authors should use SWI OS_SpriteOp and the font manager SWIs instead.

SPRITE OPERATIONS

Printer drivers intercept OS_SpriteOp via the SpriteV vector. Most calls are simply passed through to the operating system or the SpriteExtend module. The ones that normally plot to the screen are generally intercepted and used to generate printer output by the printer driver.

The following reason codes normally involve reading or writing the screen contents and are not straightforward sprite plotting operations. Because some printer drivers redirect output to a sprite internally, it is unknown what the "screen" is during these operations. They are therefore faulted.

2 - screen save
3 - screen load
14 - get sprite from current point on screen 16 - get sprite from specified point on screen

Reason codes that are passed through to the operating system or the SpriteExtend module are:

8 - read sprite area control block
9 - initialise sprite area
10 - load sprite file
11 - merge sprite file
12 - save sprite file
13 - return name of numbered sprite
15 - create sprite
25 - delete sprite
26 - rename sprite
27 - copy sprite
29 - create mask
30 - remove mask
31 - insert row
32 - delete row
33 - flip about X axis
35 - append sprite
36 - set pointer shape
40 - read sprite size
41 - read pixel colour
42 - write pixel colour
43 - read pixel mask
44 - write pixel mask
45 - insert column
46 - delete column
47 - flip about Y axis
62 - read save area size

The following reason code is passed through to the operating system when it is called for a user sprite (i.e. with &100 or &200 added to it), as this call is simply asking the operating system for the address of the sprite concerned. If the system version is called (i.e. without anything added to it), it is asking for a sprite to be selected for use with the VDU sprite plotting sequences. As these sequences are not handled by the printer driver, this version of the call generates an error.

24 - select sprite

The following reason codes plot a sprite or its mask, and are converted into appropriate printer output:

28 - plot sprite at current point on screen 34 - plot sprite at specified point on screen 48 - plot mask at current point on screen 49 - plot mask at specified point on screen 50 - plot mask at specified point on screen, scaled 52 - plot sprite at specified point on screen, scaled 53 - plot sprite at specified point on screen, grey scaled

The following reason code is mainly used by the VDU drivers to implement sizes other than 8x8 and 8x16 for VDU 5 characters. It is not handled by the printer drivers (which deal with scaled VDU 5 text by another mechanism) and causes an error if encountered during a print job.

51 - plot character, scaled

As usual for a printer driver, only some GCOL actions are understood. If the GCOL action is not divisible by 8, nothing is plotted. If it is divisible by 8, the "overwrite" action is used. If it is divisible by 16, the sprite is plotted without using its mask; otherwise the mask is used.

The colours used to plot sprite pixels are determined as follows:

      SWI ColourTrans_SelectTable with R2 = -1
      SWI ColourTrans_ReturnColourNumber
      SWI ColourTrans_ReturnColourNumberForMode with R1 = -1
      SWI ColourTrans_ReturnOppColourNumber
      SWI ColourTrans_ReturnOppColourNumberForMode with R1 = -1

It can therefore look up precisely which RGB combination is supposed to correspond to each sprite pixel value. Because of the variety of ways in which printer drivers can allocate these values, the translation table should always have been set up in the current print job and using these calls.

If a sprite is printed unscaled, its size on the printed output is the same as its size would be if it were plotted to the screen in the screen mode that was in effect at the time that the print job concerned was started. If it is printed scaled, the scaling factors are applied to this size. This is one of the few ways in which the printed output does depend on this screen mode (the main other ones are in interpreting GCOL and TINT values, and in interpreting VDU 5 character sizes). It is done this way in the expectation that the application is scaling the sprite for what it believes is the current screen mode.

Finally, the following two reason codes are intercepted to keep track of whether plotting output is currently supposed to go to a sprite or to the screen. If it is supposed to go to a sprite, it really will go to that sprite

60 - switch output to sprite
61 - switch output to mask

DRAW MODULE CALLS

Printer drivers intercept the DrawV vector and re-interpret those calls whose purpose is to plot something on the screen, producing appropriate printer output instead. There are a number of restrictions on the calls that can be dealt with, mainly due to the limitations of PostScript. Most of the operations that are disallowed are not particularly useful, fortunately.

Note that the Draw module calls normally use the graphics foreground colour to plot with and the graphics origin. The printer driver uses its versions of these values. In particular, this means that the fill colour is subject to all the restrictions noted elsewhere in this document.

The floating point Draw module calls are not intercepted at present. If and when the Draw module is upgraded to deal with them, printer drivers will be similarly upgraded.

SWI Draw_Fill

Printer drivers can deal with most common calls to this SWI. The restrictions are:

(a) They cannot deal with fill styles that invoke the positive or negative

      winding number rules - i.e. those with bit 0 set.
(b) They cannot deal with a fill style which asks for non-boundary exterior
      pixels to be plotted (i.e. which have bit 2 set). Exception: they can
      deal with the trivial case in which all of bits 2-5 are set - i.e. if
      all pixels in the plane are to be plotted!
(c) They cannot deal with the following values for bits 5-2:
        0010 - plot exterior boundary pixels only.
        0100 - plot interior boundary pixels only.
        1010 - plot exterior boundary and interior non-boundary pixels only.
(d) An application should not rely on there being any difference between
      what is printed for the following three values of bits 5-2:
        1000 - plot interior non-boundary pixels only.
        1100 - plot all interior pixels.
        1110 - plot all interior pixels and exterior boundary pixels.
      (A printer driver will generally try its best to distinguish these, but
      it may not be possible.)

SWI Draw_Stroke

Again, most common calls to this SWI can be dealt with. The restrictions on the parameters depend on whether the specified thickness is zero or not:

If the specified thickness is zero, the restrictions are:

(a) Printer drivers cannot deal with a fill style with bits 3-2 equal to 01

If the specified thickness is non-zero, the restrictions are:

(a) All the restrictions mentioned under SWI Draw_Fill above. (b) They cannot deal with bits 5-2 being 0110 - i.e. asking for just the

      boundary pixels of the resulting filled path to be plotted.
(c) Most printer drivers will not pay any attention to bit 31 of the fill
      style - the one that distinguishes plotting the stroke subpath by
      subpath from plotting it all at once.

SWI Draw_StrokePath, SWI Draw_FlattenPath and SWI Draw_TransformPath

None of these do any plotting; they are all dealt with in the normal way by the Draw module.

SWI Draw_ProcessPath

This SWI is faulted if R7=1 (fill path normally) or R7=2 (fill path subpath by subpath) on entry. Use the appropriate one of Draw_Fill or Draw_Stroke if you want to produce printed output. If the operation you're trying to do is too complicated for them, it almost certainly cannot be handled by e.g. the PostScript printer driver.

All other values of R7 correspond to calls that don't do any plotting and are dealt with in the normal way by the Draw module. If you're trying to do something complicated and you've got enough workspace and RMA, a possible useful trick is to use SWI Draw_ProcessPath with R7 pointing to an output buffer, followed by SWI Draw_Fill on the result.

COLOURTRANS MODULE CALLS

The printer driver intercepts calls to the ColourTrans module, via the ColourV vector. Most of them are passed straight on to the ColourTrans module

SWI ColourTrans_SelectTable with R2 = -1

Each RGB combination in the source palette (or implied by it in the case of 256 colour modes) is converted into a colour number as though by SWI ColourTrans_ReturnColourNumber (see below). The resulting values are placed in the table.

SWI ColourTrans_SetGCOL

The printer driver's version of the foreground or background colour (as appropriate) is set. The GCOL actions are interpreted precisely as for the VDU 18,k,c call (see above). However, rather than looking up a GCOL in the screen palette at plot time, the exact RGB combination specified in this call is remembered and used (as accurately as the printer will render it) at plot time.

After this has been done, the call is effectively converted into SWI ColourTrans_ReturnGCOL and passed down to the ColourTrans module in order to set the information returned correctly. Note that this implies that subsequently using the GCOL returned in a VDU 18,k,c sequence will not produce the same effect on the colour as this call: it will merely produce the best approximation the printer can manage to the best approximation the current screen palette can manage to the specified RGB combination. It is therefore probably a bad idea to use the values returned.

This call therefore allows the application to make full use of a printer's colour resolution without having to switch to another screen mode or mess around with the screen's palette, and without worrying about the effects of a change in the screen's palette. It is therefore the recommended way to set the foreground and background colours.

SWI ColourTrans_ReturnColourNumber

This will return a code value (in the range 0-255) that identifies the specified RGB combination as accurately as possible to the printer driver. How this code value is determined may vary from printer driver to printer driver, and indeed even from print job to print job for the same printer driver. An application should therefore not make any assumptions about what these code values mean.

(Most printer drivers implement this by pre-allocating some range of code values to evenly spaced RGB combinations, then adopting the following approach:

(a) If the RGB combination is already known about, return the

       corresponding code value.
(b) If the RGB combination is not already known about and some code values
       are still free, allocate one of the unused code values to the new RGB
       combination and return that code value.
(c) If the RGB combination is not already known about and all code values
       have been allocated, return the code number whose RGB combination is
       as close as possible to the desired RGB combination.

The pre-allocation of evenly spaced RGB combinations will ensure that even case (c) does not have really terrible results.)

SWI ColourTrans_ReturnColourNumberForMode with R1 = -1

This is treated exactly the same as SWI ColourTrans_ReturnColourNumber above.

SWI ColourTrans_SetOppGCOL

This behaves like ColourTrans_SetGCOL above, except that the RGB combination it remembers is the furthest possible RGB combination from the one actually specified in R0, and it ends by being converted into a call to ColourTrans_ReturnOppGCOL. Note that there is no guarantee that the GCOL returned is anywhere near the RGB combination remembered!

SWI ColourTrans_ReturnOppColourNumber

This behaves exactly as though ColourTrans_ReturnColourNumber (see above) had been called with R0 containing the furthest possible RGB combination from the one actually specified.

SWI ColourTrans_ReturnOppColourNumberForMode with R1 = -1

This behaves exactly as though ColourTrans_ReturnColourNumberForMode (see above) had been called with R1 = -1 and R0 containing the furthest possible RGB combination from the one actually specified.

SWI ColourTrans_SetFontColours

The printer driver's version of the font colours is set, to as accurate a representation of the desired RGB combinations as the printer can manage.

After this has been done, the call is effectively converted into SWI ColourTrans_ReturnFontColours and passed down to the ColourTrans module in order to set the information returned correctly. Note that this implies that subsequently using the values returned in a SWI Font_SetFontColours call will not produce the same effect on the font colours as this call: it will merely produce the best approximations the printer can manage to the best approximations the current screen palette can manage to the specified RGB combinations. It is therefore probably a bad idea to use the values returned.

This call therefore allows the application to make full use of a printer's colour resolution without having to switch to another screen mode or mess around with the screen's palette, and without worrying about the effects of a change in the screen's palette. It is the recommended way to set the font colours.

FONT MANAGER CALLS

The printer driver interacts with the font manager (via a service call and SWI PDriver_FontSWI) in such a way that when it is active, calls to the following SWIs are processed by the printer driver:

SWI Font_Paint
SWI Font_LoseFont
SWI Font_SetFontColours
SWI Font_SetPalette

This enables the printer driver to make SWI Font_Paint produce printer output rather than affecting the screen.

The use of SWI Font_SetFontColours is not recommended, as it results in the setting of colours that depend on the current screen palette. Instead, use SWI ColourTrans_SetFontColours to set font colours to absolute RGB values. Similarly, the use of colour-changing control sequences in strings passed to SWI Font_Paint is not recommended.

In addition to the control sequences allowed by current font managers, printer drivers will be able to handle the following control sequence if the font manager they are working with can:

19,background red,green,blue,foreground red,green,blue,maximum offset

This gives a way of changing colour in the middle of a string without sacrificing colour resolution.

(How exactly it does this varies quite markedly between printer drivers: for instance, most dot matrix printer drivers will probably use the font manager to write into the sprite they are using to hold the current strip of printed output, while the PostScript printer driver uses the PostScript prologue to define a translation from font manager font names to printer fonts.)

MISCELLANEOUS CALLS

OS_Byte 163,242,0-64 are intercepted to set the printer driver version of the dot pattern repeat length instead of the VDU drivers' version.

OS_Byte 218 is intercepted to act on the printer driver's VDU queue instead of the VDU drivers' version.

It should be noted that most of the informational calls associated with the VDU drivers (and OS_ReadVduVariables in particular) will produce undefined results when a printer driver is active. These results are likely to differ between printer drivers (in particular, they will vary according to whether the printer driver plots to a sprite internally and if so, how large the sprite concerned is).

The only informational calls that the application may rely upon are:

   OS_Word 10      - used to read character and ECF definitions.
   OS_Word 11      - used to read palette definitions.
OS_ReadPalette - used to read palette definitions.
   OS_Byte 218     - when used to read the number of bytes in the VDU queue.

ERROR HANDLING

There are a couple of somewhat unusual features about the printer drivers' error handling that an application author should be aware of:

First, ESCAPE condition generation and side effects are turned on within various calls to the printer driver and restored to their original state afterwards. If the application has ESCAPE generation turned off, it is guaranteed that any ESCAPE generated within the print job will be acknowledged and turned into an 'Escape' error. If the application has ESCAPE generation turned on, most ESCAPEs generated within the print job will be acknowledged and turned into 'Escape' errors, but there is a small window at the end of the call during which an ESCAPE will not be acknowledged. If the application makes a subsequent call of one of the relevant types to the printer driver, that subsequent call will catch the ESCAPE. If no such subsequent call is made, the application will need to trap the ESCAPE itself.

The SWIs during which ESCAPE generation is turned on are:

PDriver_SelectJob for a new job
PDriver_EndJob
OS_WriteC
All ColourTrans SWIs
Draw_Fill
Draw_Stroke
Font_SetFontColours
Font_SetPalette
Font_Paint
OS_SpriteOp with reason codes: PutSprite

                                 PutSpriteUserCoords
                                 PutSpriteScaled
                                 PutSpriteGreyScaled
                                 PlotMask
                                 PlotMaskUserCoords
                                 PlotMaskScaled

All but the first two only apply at times when the printer driver is intercepting plotting calls - i.e. at times when all of the following conditions hold:
(a) There is an active print job.
(b) Plotting output is directed either to the screen or to a sprite

      internal to the printer driver.
(c) The Wimp is not reporting an error (as defined by the service call
      with reason WimpReportError).

Secondly, inside a number of calls, any error that occurs is converted into a "persistent error". The net effect of this is that: (a) The error number is left unchanged. (b) The error message has the string " (print cancelled)" appended to it.

      If it is so long that this would cause it to exceed 255 characters, it
      is truncated to a suitable length and "... (print cancelled)" is
      appended to it.
(c) Any subsequent call to any of the routines concerned will immediately
      return the same error.
The reason for this behaviour is to prevent errors the application is not expecting from being ignored. (E.g. quite a lot of code assumes incorrectly that OS_WriteC cannot produce an error. This ensures that an error during OS_WriteC cannot reasonably get ignored forever.)

The SWIs during which persistent errors are created are:

PDriver_EndJob
PDriver_GiveRectangle
PDriver_DrawPage
PDriver_GetRectangle
OS_WriteC
All ColourTrans SWIs
Draw_Fill
Draw_Stroke
Draw_ProcessPath with R7=1
Font_SetFontColours
Font_SetPalette
Font_Paint
OS_SpriteOp with reason codes: PutSprite

                                 PutSpriteUserCoords
                                 PutSpriteScaled
                                 PutSpriteGreyScaled
                                 PlotMask
                                 PlotMaskUserCoords
                                 PlotMaskScaled
                                 ScreenSave
                                 ScreenLoad
                                 GetSprite
                                 GetSpriteUserCoords
                                 PaintCharScaled
                                 SelectSprite (system version only)
                                 Reason codes unknown to the printer driver

All but the first four only apply at times that the printer driver is intercepting plotting calls - see above for details of this.

SWI PDriver_CancelJob puts a print job into a similar state, with the error message being simply "Print cancelled". However, this error is only returned by subsequent calls from the list above - not by PDriver_CancelJob itself.

Note that an application must respond to any error during a print job that could have come from one of the above sources by calling PDriver_AbortJob. In particular, take care to respond to errors from PDriver_EndJob by calling PDriver_AbortJob, not PDriver_EndJob - otherwise an infinite succession of errors will occur or an unfinished print job will be left around.