Font downloading to PostScript printers

The original fonts provided with RISC OS were specifically chosen to correspond with the fonts on the Adobe LaserWriter, so that when printing to a PostScript printer, the built-in fonts could be used directly, and no actual font downloading was necessary.

Since then various third parties have constructed new fonts, that are not present on the LaserWriter, and so it has become necessary to download fonts.

By using the Adobe internal font format (recently published in the so-called 'black book'), it will be possible to download fonts more efficiently than at present, and it may also be possible to preserve hinting information.

There are two main ways of downloading font information to a PostScript printer - one is to download a copy of the font with each print job that uses it, and another is to download a single copy of each font, so that it remains resident in the printer for all subsequent print jobs.

Both methods have their drawbacks - the first involves a lot of data transfer, as each job must download the fonts it needs, while the second consumes large amounts of memory in the printer, especially if there is a large variety of fonts to be downloaded.

I believe that we should support both methods, as they are each suitable for certain scenarios (the first when there are a lot of print jobs using 'special' fonts, and the second where a manual is being printed in separate sections, all of which use the same font which is not resident by default.

This will involve certain changes to the application printing interface, since it is necessary (due to the Adobe print job Structuring Conventions) to declare all fonts to be used before any pages are printed.

In addition, if the printer driver is to automatically download fonts within print jobs, it must be made aware of which fonts have already been permanently downloaded into the printer, so it can ignore them. This could be done by user intervention on the client machine, but is better done by interrogating the printer server.

I propose that the work in this area be executed in three stages, where the output of each stage could be used as a stepping-off point, but where each stage constitutes a significant advance over the previous one:

Stage 1

!PrinterPS includes an option to allow the user to download a font to the printer (which can then be used by all subsequent print jobs). If the printer has a non-zero 'exitserver' password, the user would have to enter it. This prevents unauthorised users from clogging-up shared printers with fonts.

Note that the user is responsible for explicitly downloading any fonts that will be required by a subsequent print job - no automatic checking is done.

Stage 2

As well as the explicit permanent downloading of fonts, applications can be improved to declare the fonts they are going to use to the printer driver module. If any fonts in the current print job are not already loaded in the printer, the printer driver dynamically converts the relevant fonts to PostScript, and downloads them within the job. The fonts cannot be used by subsequent print jobs, and the memory they occupy in the printer will be freed as soon as the job finishes.

The printer driver needs to know:

a) Which fonts have already been permanently downloaded b) Which fonts to download if the application does not declare any

Because not all applications will immediately be upgraded to declare the fonts they will use, it is necessary to allow the user to specify this explicitly in some cases. Normally the list of fonts to download will be limited to those fonts that can simply be mapped directly onto existing PostScript fonts (eg. Trinity.Medium maps onto Times-Roman).

The list of permanently downloaded fonts can be automatically updated if a new font is permanently downloaded, but unfortunately this does not cope with the printer being reset, or someone else downloading a font to the printer from somewhere else on the network.

Stage 3

In order to hide the issues of which fonts are already downloaded from the naïve user, the printer driver will have to be able to find out which fonts are already downloaded automatically.

The established method of finding this out is to send a 'query job' to the printer, which is a PostScript job that causes no pages to be printed, but send some output back to the sender (in this case a list of the permanently downloaded fonts).

This can only happen if the printer is able to talk back to the client, which is not always possible (eg. if the printer is off-site, and the job is being printed to a file), so it will still be necessary for a user-supplied default to be used.

When talking to a printer that is controlled by a spooler, another problem arises - the printer might be busy printing another job. To get round this, the query job contains some 'structured comments', which allow the spooler software to recognise it itself, and send back an appropriate reply if the printer is busy. The spooler can know which fonts are already downloaded by recognising the structured comments that occur in font download jobs.

Work involved

The above is a relatively brief summary of the mechanisms that could be used to achieve automatic font downloading to PostScript printers.

The following sections go through the mechanisms at a lower level of detail, in an attempt to assess the amount of work involved.

Stage 1 in more detail

We need a utility that can convert from the Risc OS file format to the Adobe type 1 internal font format, and a front-end that allows the user to specify the font(s) to download.

The front end would probably reside within !PrinterPS, since the destination printer must be specified, which is already done within that program.

Most of the work for this stage is to write a convertor that takes a Risc OS outline font and converts it to PostScript. Adobe have recently published their 'type 1' internal format, which includes hint information, so it may be possible to convert our hint information too. Further elaboration is impossible without seeing the contents of the Adobe 'black book', detailing the type 1 format.

Since some 'PostScript' printers may not understand the Adobe type 1 format, we should also allow conversion to 'user' PostScript fonts, using only legal PostScript constructs. These fonts will be bigger, slower and will probably lack hints, however.

Note that the output from the downloader would have to include the exitserver password, entered by the user, and some structured comments as defined by the Adobe Structuring Conventions.

This converter would initially be written in C.

Stage 2 in more detail

The PDriverPS module provides a new SWI PDriver_DeclareFont that is capable of converting from outline fonts to the equivalent Adobe type 1 format PostScript. This can be called by applications to declare the fonts that are to be used within a job, and also by the permanent font downloader, which passes in the exitserver password.

        SWI PDriver_DeclareFont
        In      R0 = print job handle (or handle of destination file)
                R1 -> font name (0 => no fonts in print job)
                R2 = exitserver password
                     or -1 => download within job
        Out     Adobe type 1 version of font sent to handle in R0
        If the R2 on entry is not -1, the SWI should be called outside the
        context of a print job, to permanently download the font.  The
        "exitserver password" is an integer that is required in order to
        execute the exitserver command in PostScript, which in turn allows
        the font to be retained for subsequent print jobs.
        If R2 = -1 on entry, the SWI should be called after
        PDriver_SelectJob and before the first PDriver_DrawPage, once for
        each font that will be used in the job.
        The printer driver looks in "PDriverPS:FontNames" to find out how to
        download a font.  The file would look something like this:
                Corpus.Medium           Courier                 Latin1
                Corpus.Medium.Oblique   Courier-Oblique         Latin1
                Corpus.Bold             Courier-Bold            Latin1
                Corpus.Bold.Oblique     Courier-BoldOblique     Latin1
                Selwyn                  ZapfDingbats            Selwyn
                Sidney                  Symbol                  Sidney
                Trinity.Medium          Times-Roman             Latin1
                Trinity.Medium.Italic   Times-Italic            Latin1
                Trinity.Bold            Times-Bold              Latin1
                Trinity.Bold.Italic     Times-BoldItalic        Latin1
                etc.
        All fonts that have a known PostScript equivalent that is resident
        in the default LaserWriter should be in here - when a font is to be
        downloaded within a job, PDriverPS looks in here to see if the
        mapping can be done by mapping onto an existing font.
        If the font name is found, then the file
        "PDriverPS:Encodings.<encoding>" is downloaded (unless it has
        already been done for this print job), and a small section of
        PostScript that creates (eg) a font called "Acorn-Trinity.Medium"
        that is like "Times-Roman" but with a new encoding.
        If the font name is not found, then the downloading must be done the
        hard way, by converting from the outlines to the Adobe type 1
        format.
        If R2 <> -1 on entry, then the whole lot is surrounded by an
        "exitserver" command, so that the results are made permanent.

A file of which fonts have been downloaded already should be associated with each printer known to the system. Each printer on the network has a name that can be determined by sending a message to it. NetPrint needs to be extended to allow the printer name for a given file handle to be determined (with a default name if this is not a NetPrint file).

PDriverPS then reads from "PDriverPS:Downloaded.<printername>", which contains a list of fonts that should be treated as being already present in the LaserWriter (so should be ignored if passed to PDriver_DeclareFont).

If no fonts are declared at the start of the print job, the printer driver will look in the file "PDriverPS:Default" to see which fonts to download. Note that if a new-style application realises that there are no fonts in the job to be printed, it should make a single call to PDriver_DeclareFont to stop the printer driver module from downloading the default set.

The printer driver front end allows the user to select the set of fonts that will be 'downloaded' if the application doesn't declare any fonts. This is written to the file "<PDriverPS$Dir>.Default", and read as "PDriverPS:Default" by the printer driver module.

When a font is permanently downloaded, the file "<PDriverPS$Dir>.Downloaded.<printername>" is updated so that the printer driver knows not to download that font again when sending print jobs to that printer.

Stage 3 in more detail

In order for the PDriverPS module to interrogate the printer, it will have to be able to find out where the current print job destination is, and send a query job to it. This causes several problems:

In fact these problems can be solved if we assume that the user can only set the print destination using !PrinterPS, and that, for all types of output except file printing, the file handle will have been obtained by opening "Printer:" for output.

Given these assumptions all the printer driver has to do is spot the following special cases:

                                          query job
        print job output        output stream   input stream
        ----------------        -------------   ------------
        parallel                "parallel:"     "serial:"
        serial                  "serial:"       "serial:"
        network                 "netprint:"     "netprint:"
        file                    <none>          <none>

Note that if the parallel port is being used for output, the serial port can still be used to receive data from the PostScript printer.

In all cases where a query job might be sent, it is possible that the printer might not be able to return any data (eg. if the serial port is not connected), so the printer driver must be able to recognise this and revert to the "default" set of fonts to be considered as already downloaded (held in the file "PDriverPS:Downloaded.<printername>").

This means that NetPrint must be improved so that "NetPrint:" can be opened for input, to receive data back from the printer.

Theoretically it would also be better if multiple input and output streams were possible, and also a 'broadcast' input stream, that picked up data from a printer that was not associated with another open stream. This would allow a logging program to display the normal output of the PostScript printer (such as "Out of paper" error messages) in a window.

Another improvement to NetPrint, which is not strictly essential to font downloading, would be to make it double-buffer its data, so it could receive data into one buffer while attempting to send the previous buffer in the background. This allows the printer driver to do conversion work while NetPrint is waiting for the network to become free.

Two further problems occur when the printer is to be accessed via a spooler:

  1. The spooler needs to be able to send output from the printer back to the client.
  2. The printer might be busy printing another job, and you don't want to have to queue the query job and wait till it executes. The first problem can be solved as follows:
            The spooler should send all output from the printer back to the
            client that sent the original job.  This is desirable for other
            reasons too, as things like error reports from the printer should be
            sent back to the job anyway.
    
            The data coming back must be tagged with a reference handle that
            is passed in with the original print job, so that the client knows
            which print job each piece of data is associated with.  This is
            important since when sending a query job, other print jobs may still
            be executing and sending back data, which must not be confused with
            the query job results.
    
            PostScript printers send back a ctrl-D when the job has finished,
            which should be returned to the client, so it knows when it is OK to
            close the input stream.
    
            If a client is not listening (NB retries should be done in the
            background), the data should be thrown away (ie. no error generated
            in the spooler).
    

    The second problem can be solved as follows:

            A query job contains a piece of PostScript that finds out the names
            of all the fonts in the machine, and writes them out to the client.
    
            The job also contains some "structured comments", which can be
            recognised by the spooler software as a query job which is trying to
            send back the names of all the fonts in the machine.  Font
            downloading jobs also contain (different) structuring information,
            which tells the spooler that it should recache the list of
            downloaded fonts (by sending a query job to the printer).
    
            Then, if the PostScript printer is stand-alone, the PostScript job
            will execute normally and return the desired information.
    
            If the spooler is controlling the printer, it will pick up the query
            job and deal with it itself, by sending back its cached list of the
            fonts in the printer.  This means that it doesn't matter if the
            printer is currently busy.
    

    Note that Acorn's current printer spooler software has never been released to the outside world, so the first spooler that the punters are likely to see is the one in the Acorn Level 4 Fileserver.

    Summary

    Work in stage 1:

            Front end in !PrinterPS:        1 man week
            Font to PostScript Convertor:   6 man weeks
    

    Work in stage 2:

            PDriver_DeclareFont:            2 man weeks
            Front end in !PrinterPS:        2 man weeks
    

    Work in stage 3:

            PDriver code:                   3 man weeks
            Improvements to NetPrint:       3 man weeks
            Improvements to spooler:        3 man weeks