/* Original Author:         Andrew Trevorrow
   Implementation: Modula-2 under VAX/UNIX 4.2 BSD
   Date Started:   June, 1986

   Description:
   DVIReader exports routines and data structures for moving about randomly
   in a TeX82 DVI file and interpreting pages.
   DVItoVDU (an interactive page previewer) is currently the only client,
   but it is anticipated that DVIReader could just as well form the basis of a
   more conventional DVI translator (such as a non-interactive device driver).

   Limitations:
   - DVIReader does not look at the preamble and so ignores any comments there.
   - DVIReader does not do any terminal i/o, even upon detecting a fatal error.
     The client must write a DVIErrorRoutine that will be invoked at the time
     an error is detected.  See below for details.
   - DVIReader knows little about the format of PXL, RST or similar such files.
     It only knows they contain crucial information for typesetting a DVI file.
     The client must write a PixelTableRoutine to read some sort of font file
     and fill in the pixel table information used to interpret a DVI page.
     See below for details.
   - The data structures used to represent an interpreted DVI page are best
     suited to a client whose output device is randomly addressable.
     Some clients may need to convert the information in the data structures
     into a more suitable format (such a task may even be impractical).

   Algorithm for a simple DVI translator:
      < initialize the DVIErrorRoutine used to handle various errors >
      < initialize filespec, resolution, etc. >
      OpenDVIFile(filespec);
      < initialize magnification, probably with DVImag >
      SetConversionFactor(resolution,magnification);
      < initialize the SpecialRoutine used in InterpretPage >
      < initialize the PixelTableRoutine used in InterpretPage >
      FOR p := 1 TO totalpages DO
         MoveToDVIPage(p);
         InterpretPage;
         IF pageempty THEN
            < process an empty page >
         ELSE
            < check that the page edges are within the paper edges >
            < process the rule list >
            < process the character list for each font used on the page >
         END;
      END;
      CloseDVIFile;

-------------------------------------------------------------------------------
   This version converted to C and ported to BSD and System V UNIX by
   some chaps at Kernel Technology up to September 1989.  Contact
   mjh@uk.co.kernel (Mark J. Hewitt) with bug fixes etc.

   Involved were:	Mark J. Hewitt
   			Dave Dixon
			Marc Hadley
*/

static char *sccsid_dvir[] = "@(#)dvireader.h	1.1";

#define   RULETABLESIZE  300     /* maximum number of rules in a ruletable   */
#define   CHARTABLESIZE  3000    /* maximum number of chars in a chartable   */
#define   MAXFONTSPEC    82      /* maximum length of a font file spec       */
#define   MAXTEXCHAR     129     /* DVI and font files should use char codes */
                                 /* in 0..127 */
                                 /* (Knuth's TRIP.DVI has codes > 127!)     */

   /* The major page structures are defined first.
      Information about the rules and characters appearing on a page is stored
      in dynamic one-way lists to avoid imposing any limit on their numbers.
      To reduce pointer overheads, the nodes in these lists contain large
      tables (the values of RULETABLESIZE and CHARTABLESIZE have been chosen
      so that the vast majority of DVI pages will only require one-node lists).
      When interpreting a DVI page, DVIReader adds a new rule or character node
      to the TAIL of the relevant list.  This is done so that when the client
      accesses such lists (starting at the head), rules and characters will be
      processed in somewhat the same sequence as seen in the DVI file; i.e.,
      top-to-bottom and left-to-right across the page.
      Since a character list is part of the information stored for a font,
      the precise sequence in which DVI characters are seen is not remembered.
      Font information is also linked together in a one-way list, but the
      ordering is more or less random (see, however, the SortFonts routine).
   */

   struct  ruleinfo {                     /* a node in a list of ruletables  */
           unsigned int rulecount ;       /* number of rules in ruletable    */
           struct ruletab {
                  int hp, vp ;            /* pixel coords of rule's ref point*/
                  int wd, ht ;            /* dimensions of rule in pixels    */
           } ruletable[RULETABLESIZE];
           struct ruleinfo  *nextrule ;   /* next node in rule list          */
   } ;

   typedef struct  ruleinfo  *ruleinfoptr ;

   struct  charinfo {                     /* a node in list of chartables    */
           unsigned int charcount ;       /* number of chars in chartable    */
           struct chartab {
                 int    hp, vp ;          /* pixel coords of char's ref point*/
                 short  code;             /* char's code and pixeltable index*/
           } chartable[CHARTABLESIZE];
           struct charinfo  *nextchar ;   /* next node in char list          */
   } ;

   typedef struct  charinfo  *charinfoptr ;

   /* pixeltable must be filled in by the client's PixelTableRoutine.
      DVIReader uses wd, ht, xo and yo to calculate minhp, minvp, maxhp and
      maxvp.  It uses dwidth and pwidth to advance horizontally after
      setting a character.  The mapadr field is not used by DVIReader.
   */
      typedef struct pixtabstr {
         int  wd, ht ;    /* glyph width and height in pixels; they
                                 define the size of the smallest box
                                 containing all the black pixels             */
         int  xo, yo ;    /* x and y offsets from top left corner
                                 of glyph to character's reference point     */
         int  dwidth ;    /* advance width in DVI units computed from
                                 fix width stored in font file               */
         int  pwidth ;    /* advance width in pixels computed from
                                 fix width stored in font file               */
         unsigned int mapadr ;   /* starting address of pixel map in font file
                                 (could be a word or byte offset; DVIReader
                                 neither knows nor cares)                    */
      } pixtabstr;

   typedef struct pixtabstr *pixeltableptr ;

   typedef char  fontstring[MAXFONTSPEC] ;

   typedef struct    fontinfo   *fontinfoptr;

      struct  fontinfo     {            /* a node in list of fonts           */
              short fontused;           /* is font used on current page?     */
              int   fontnum;            /* DVI font number: -2^31 .. 2^30 - 1*/
              unsigned int  scaledsize; /* scaled font size in DVI units     */
              unsigned int  designsize; /* design size in DVI units          */
              fontstring    fontarea;   /* explicit font directory           */
              unsigned int  fontarealen;/* length of fontarea                */
              fontstring    fontname;   /* font name; e.g., "cmr10"          */
              unsigned int  fontnamelen;/* length of font name               */
              fontstring    fontspec;   /* client's font file specification  */
              unsigned int  fontspeclen;/* length of fontspec                */
              unsigned int  totalchars; /* number of chars from font on page */
              charinfoptr   charlist;   /* head of char information list     */
              charinfoptr   chartail;   /* tail of char information list     */
              pixeltableptr pixelptr;   /* allocated once: 1st time font used*/
              fontinfoptr   nextfont;   /* next node in font list            */
      };

   /* For the parameter in MoveToTeXPage: */
   typedef   int  TeXcounters[10];

      struct TeXpageinfo {
             TeXcounters    value ;           /* \count0..\count9 values    */
             short          present[10] ;      /* is counter relevant?      */
             short          lastvalue ;       /* last relevant counter      */
      };

   /* For the parameter in client's DVIErrorRoutine: */

typedef unsigned short DVIerrorcodes;

#define DVIunopened      0x00  /* OpenDVIFile could not open the given file  */
#define DVIempty         0x01  /* OpenDVIFile detected an empty file         */
#define DVIbadid         0x02  /* OpenDVIFile found an invalid TeX82 DVI file*/
#define DVIstackoverflow 0x03  /* OpenDVIFile found DVIReader's stck too smll*/
#define DVIbadchar       0x04  /* InterpretPage is ignoring a */
                               /* char code > MAXTEXCHAR */
                               /* (found while processing currfont^)*/
#define DVIcatastrophe   0x05  /* DVIReader found a situation that should */
                               /* never occur; client should scream */
                               /* for help and halt*/

   /* For the function parameter in client's SpecialRoutine: */
typedef  int (*GetByteFunction)() ;   /* returns next DVI byte       */

   /* Most of these should be treated as read-only parameters:               */
unsigned int
   DVImag,                        /* magnification stored in DVI file        */
   totalpages,                    /* number of pages in DVI file             */
   totalfonts,                    /* number of fonts in DVI file             */
   currDVIpage;                   /* updated by MoveTo... calls              */

TeXcounters
   currTeXpage;                   /* ditto                                   */

unsigned int
   totalrules;                    /* number of rules on current page         */

ruleinfoptr
    rulelist,                     /* head of rule information list           */
    ruletail;                     /* tail of rule information list           */

fontinfoptr
    fontlist,                    /* head of font information list           */
    currfont;                    /* InterpretPage's current font info       */

short
   pageempty;                    /* is page empty of rules and chars?       */

int
   minhp,                        /* minimum horizontal pixel coordinate     */
   minvp,                         /* minimum vertical pixel coordinate       */
   maxhp,                         /* maximum horizontal pixel coordinate     */
   maxvp;                         /* maximum vertical pixel coordinate       */

void InitReader();

Void (*DVIErrorRoutine)();
   /* The client MUST assign a procedure before the first OpenDVIFile call.
      Various DVIReader routines will call this procedure if they detect some
      sort of problem; the given argument will be one of the error codes
      described above.
      The client's routine can use this code to see what sort of error
      has occurred and take appropriate action.  Most errors detected by
      DVIReader are fatal; the client should print some sort of message
      and halt.  See DVItoVDU for an example DVIErrorRoutine.
   */

Void (*SpecialRoutine)();
   /* The client can assign a procedure before the first InterpretPage call.
      InterpretPage will call this procedure to process the bytes belonging to
      a \special command.  The first argument gives the number of bytes; the
      second argument is a function that returns the value of the next byte
      (0..255, but usually the ordinal value of a displayable ASCII character).
      The client's routine might look something like:
          PROCEDURE MySpecialRoutine (totalbytes : INTEGER;
                                      NextByte   : GetByteFunction);
          VAR i, nextval : INTEGER;
          BEGIN
          FOR i := 1 TO totalbytes DO
             nextval := NextByte();
             < process nextval >
          END;
          END MySpecialRoutine;
      If the client does not supply a SpecialRoutine, DVIReader will simply
      ignore any \special bytes.
   */

Void (*PixelTableRoutine)();
   /* The client MUST assign a procedure before the first InterpretPage call.
      InterpretPage will call this procedure immediately after allocating a
      pixel table for currfont^.  This will only occur once per font (the
      very first time the font is used).
      DVIReader only knows about DVI files; the task of the PixelTableRoutine is
      to fill in the current font's pixel table by reading the fontspec file.
      (The fontspec string must first be built using the fontarea and fontname.)
      This file could be a PXL file, an RST file, or some other type of file.
      See DVItoVDU for an example of a PixelTableRoutine that reads a PXL file.
   */


/*PROCEDURE OpenDVIFile (filespec : ARRAY OF CHAR);*/

/* This must be the first DVIReader routine called.  If the given filespec can
   be opened (and is a valid TeX82 DVI file) then the following global
   variables are initialized:
      DVImag      := magnification value stored in DVI file (TeX's \mag)
      totalpages  := total number of pages in DVI file
      currDVIpage := 0      (and remains so until a page is selected)
      currTeXpage := ten 0s (ditto)
      totalfonts  := total number of fonts in DVI file (= nodes in font list)
      fontlist^.  (nodes are added to head of list)
         fontused   := FALSE
         fontnum    := internal DVI font number
         scaledsize := scaled size of font (in DVI units)
         designsize := design size of font (in DVI units)
         fontarea   := a string of min(fontarealen,MAXFONTSPEC) characters
         fontname   := a string of min(fontnamelen,MAXFONTSPEC) characters
         fontspec   := a null string (fontspeclen := 0)
         totalchars := 0
         charlist   := NULL
         chartail   := NULL
         pixelptr   := NULL
         nextfont   := next node in font list (if not NULL)
*/

/*PROCEDURE SetConversionFactor (resolution, magnification : CARDINAL);*/

/* This routine must be called before the first InterpretPage call.
   DVIReader needs to know the client's resolution and magnification values
   before it attempts to convert DVI dimensions into pixel values.
*/

/*PROCEDURE MoveToDVIPage  (n : CARDINAL);*/
/*PROCEDURE MoveToNextPage (ascending : BOOLEAN);*/
/*PROCEDURE MoveToTeXPage  (VAR newTeXpage : TeXpageinfo) : BOOLEAN;*/

/* Before calling InterpretPage, the client must position DVIReader to the
   desired page by calling one of these MoveTo... routines.

   MoveToDVIPage will select the nth page in the DVI file; nothing will
   happen if n is not in 1..totalpages.

   MoveToNextPage will select the next page, depending on the current page
   and the specified direction.  If the value of currDVIpage is 0 (set in
   OpenDVIFile), then MoveToNextPage will select the first page if ascending is
   TRUE and the last page if ascending is FALSE.  If currDVIpage is > 0 then
   MoveToNextPage will select currDVIpage+1 if ascending (unless currDVIpage =
   totalpages, in which case it does nothing), or currDVIpage-1 if descending
   (unless currDVIpage = 0).

   MoveToTeXPage will search for the lowest DVI page matching the given TeX page
   specification.  (TeX stores the values of \count0,\count1,...,\count9 with
   every DVI page.  Plain TeX uses \count0 to control page numbering.)
   newTeXpage is a VAR parameter only for efficiency; it won't be changed.
   The value array stores the requested counter values, the present array
   indicates which counters are relevant and lastvalue indicates
   the position (0..9) of the last relevant counter.
   The client will probably need to first convert a more friendly representation
   of a TeX page request into the TeXpageinfo format.  For example,
   [2..5] would be converted to:         and [] would be converted to:
   value     = [2,?,5,?,?,?,?,?,?,?]     value     = [?,?,?,?,?,?,?,?,?,?]
   present   = [T,F,T,?,?,?,?,?,?,?]     present   = [F,?,?,?,?,?,?,?,?,?]
   lastvalue =      2                    lastvalue =  0
   MoveToTeXPage returns TRUE iff the requested TeX page is located.

   The global variables updated if a page is located by any MoveTo... call are:
      currDVIpage := the current DVI page (1..totalpages)
      currTeXpage := the ten TeX counter values stored with this page
   Note that currDVIpage is initially 0 until one of these routines succeeds.
*/

/*PROCEDURE PixelRound (DVIunits : INTEGER) : INTEGER;*/

/* One of the important tasks the client's PixelTableRoutine must carry out
   is to convert the fix width values from a fontspec file into DVI and pixel
   units; i.e., the dwidth and pwidth values stored in the pixel table.
   Once dwidth has been calculated (see DVITYPE or DVItoVDU for how to do this)
   then pwidth is easy: pwidth := PixelRound(dwidth).
   PixelRound can be used to convert any DVI dimension (+ve or -ve) into pixels.
*/

/*PROCEDURE InterpretPage;*/

/* This routine will interpret the current DVI page and update the major
   data structures:
      totalrules := number of rules on page
      rulelist^. (nodes are added to tail of rule list)
         rulecount  := number of rules in this ruletable
         ruletable[0..rulecount-1].
            hp, vp  := reference point of a rule
            wd, ht  := pixel dimensions of a rule (both > 0)
         nextrule   := next node in rule list (if not NULL)
      ruletail   := pointer to last node in rule list

      fontlist^.
         (the following fontinfo is relevant only if fontused is TRUE)
         totalchars := number of chars on page from this font
         charlist^. (nodes are added to tail of char list)
            charcount  := number of chars in this chartable
            chartable[0..charcount-1].
               hp, vp  := reference point of a character
               code    := TeX character code (and index into pixel table)
            nextchar   := next node in char list (if not NULL)
         chartail   := pointer to last node in char list
         pixelptr^[0..maxTeXchar].
            (filled in by client's PixelTableRoutine)
            wd, ht  := glyph width and height in pixels
            xo, yo  := offsets from the character's reference point
            dwidth  := advance width in DVI units
            pwidth  := advance width in pixels
            mapadr  := starting address in fontspec of the glyph's pixel map
         nextfont   := next node in font list (if not NULL)

      pageempty  := TRUE iff the page has no rules and no characters
      minhp, minvp, maxhp, maxvp
                 := the edges of the page (undefined if pageempty is TRUE)
                    They define the smallest rectangle containing all
                    black pixels AND all reference points on the page;
                    (minhp,minvp) is the top left corner.

   Reference points for rules and characters are stored as a pair of
   horizontal and vertical pixel coordinates.  The point (0,0) is assumed
   to be the pixel 1 inch in from the top and left edges of an imaginary sheet
   of paper.  Horizontal coordinates increase to the right and vertical
   coordinates increase down the paper.
   The number of pixels per inch is defined by the resolution parameter
   given to SetConversionFactor.
*/

/*PROCEDURE SortFonts (VAR unusedlist : fontinfoptr);*/

/* This routine will sort fontlist in ascending order of totalchars.
   The client may wish to do this after interpreting a page so that fonts
   with the least number of characters on the page will be processed first.
   The routine will also move all unused fonts to the end of fontlist so that
   the client need only process fonts up to (but excluding) unusedlist
   and not have to worry about checking the fontused flag.
   For example:
       SortFont(unusedlist);
       thisfont := fontlist;
       WHILE thisfont <> unusedlist DO
          < process the font information in thisfont^ >
          thisfont := thisfont^.nextfont;
       END;
   If unusedlist is NULL then either 1) all fonts are used on the current page
   or 2) fontlist is also NULL (totalfonts = 0).
*/

/*PROCEDURE CloseDVIFile;*/

/* This routine closes the currently open DVI file; it is only mandatory if
   OpenDVIFile is going to be called again.
   All dynamic data structures are deallocated.
*/

