/*#############################################################################
 # =============================>>  dvihp.c  <<============================== #
 ##############################################################################
 #    This is the dvi driver for the HP Laserjet II, IID and IIP printers     #
 #                       for cmr fonts in .PK format                          #
 ##############################################################################
 # This program was written by                                                #
 #                                                                            #
 #                 Helmut Kopka                                               #
 #                 Max-Planck-Institut fuer Aeronomie                         #
 #                 D-3411 Katlenburg-Lindau                                   #
 #                 Tel. 05556-401451                                          #
 #                 FRG                                                        #
 #                                                                            #
 #----------------------------------------------------------------------------#
 #                     All rights reserved by H. Kopka                        #
 #----------------------------------------------------------------------------#
 #  You may copy the laserjet kit in whole or in part as long as you don't    #
 #  try to make money off it, or pretend that you wrote it.       H. Kopka    #
 #############################################################################*/

/******************************************************************************
 * !!! This program should be compiled with the '-lmalloc' linker option !!!  *
 ******************************************************************************/


/******************************************************************************
 * program strategy: DVI to device program must deal with a large amount of   *
 * data. The DVI files in most applications tend to be quite large. And, since*
 * many applications require some or part of the page to be drawn bit-by-bit, *
 * the raster image of the page itself can consume huge quantities of memory. *
 *                                                                            *
 * For this reason, one of the primary goals in the design of this program    *
 * was to reduce memory requirements as much as possible. So, it was decided  *
 * that operation of this program would occur in three distinct phases: pre-  *
 * scan, font downloading, and page drawing.                                  *
 *                                                                            *
 * Every DVI file is prescanned. During this phase, information about font    *
 * usage is collected. A very simple interpreter simply notices the 'bop' and *
 * 'eop' tokens, to allow a subset of pages to be printed, all of the         *
 * 'fnt_def' commands, during which the name and other information is stored, *
 * and the 'set_char' commands, during which certain counters are incremented.*
 * For every font that is declared, space is dynamically allocated and the    *
 * pointer to the table of information regarding that font is stored in the   *
 * 'fontptr[MAXFONTS]' pointer array.                                         *
 *                                                                            *
 * During the second phase, the font information is loaded, and possible font *
 * downloading is decided. At this point, all the information about font      *
 * usage in the file is located in the 'font' tables. Therefore, 'cachefonts' *
 * is called. This routine first determines which fonts are printer resident. *
 * For those printer resident fonts the information about fonts are taken     *
 * from the corresponding tfm_file by calling 'loadtfmfile'. 'cachefont' then *
 * tries to download as many fonts as possible. If more fonts are used in the *
 * document, the remaining fonts are stored in dynamically allocated memory   *
 * and will be drawn in graphic mode. The fonts most heavily used in the docu-*
 * ment will be downloaded first, leaving only the more rarely used fonts for *
 * graphic mode.                                                              *
 *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
 * For each font, used in the document, a structure '*font' is declared for   *
 * which memory space is allocated by 'malloc(sizeof(*font))'. The pointers   *
 * to each 'font' structure are stored in the pointer array 'fontptr[]'.      *
 * The font structure consist of an header in which information as            *
 *           checksum, scaled_size, design_size, space,                       *
 *           dir_size, height, down, name, bc and ec                          *
 * is stored, followed by an array chr[256] of character information          *
 *           pk_char, pxl_width, tfm_width, use_count                         *
 * which is filled during the prescan and downloading phase. This information *
 * is mostly used then in phase three by 'dopage()', and partly by 'cachefont'*
 * during phase two.                                                          *
 *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
 * At the end of the job, all fonts loaded during this session, will be de-   *
 * leted, giving the printers memory free for the next job.                   *
 ******************************************************************************/



#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include "paths.h"
#include "common.h"
#include "dvicom.h"
#include "hpcom.h"


#define BANNER "This is dvihp, C Version 3.14, Oct 18 1991\n"
/* The BANNER string should be changed whenever 'dvihp.c' gets modified */

#ifdef IID
#define IIP TRUE
#endif


/*==========================================================================*
 * The following definitions should be customized to your printer's memory  *
 *==========================================================================*/

#define MINLASMEM 2621440     /* Adjust this to your printer's memory.  The */
			      /* chosen value of 2.5 Mbytes is adequate for */
			      /* a printer with 4 Mbyte memory extension.   */
			      /* The absolute minimum should never be less  */
			      /* than 125 Kbytes if no graphic files will   */
			      /* be merged to the DVI output.               */
                              /* Adjust this value to your local resources  */
			      /* in correspondence to your decision for     */
			      /* `cerershp.c' and the actual MEM value of   */
			      /* the processed `reshp.fnt' file.            */

#define MAXDOWN 128           /* The Laserjet IID is not restricted to 32,  */
			      /* as the standard Laserjet II. Adjust this   */
			      /* to your local intention.                   */

/*==========================================================================*
 * The following definitions could be customized to your choice             *
 *==========================================================================*/

#define DYNAMICFONTBASE 4999  /* Starting value for internal font numbers   */
			      /* Any more font decrements by one.           */
#define HOFFSET 300           /* 1 inch left margin offset                  */
#define VOFFSET 300           /* 1 inch top margin offset                   */

#define MAXDRIFT 2            /* Maximum value for cumulaltive rounding     */
			      /*    errors in dots for output positioning.  */
#define MAXFONTS  256         /* Maximum number of usable fonts             */
#define MAXPAGES 1000         /* Maximum number of pages for the document   */

#define MAXPKSIZE 40000       /* Buffer size for PK file. Large enough to   */
			      /* load any font up to magstep8 (including    */
			      /* cminch for this magstep). Could be decrea- */
			      /* to 12000 for all fonts up to magstep8 if   */
			      /* cminch fonts are used up to magstep4 only. */

#define MAXPXLSIZE 50000      /* Buffer size for the pixel pattern of any   */
			      /* character. The chosen value is large enough*/
			      /* to maintain any size up to magstep8, for   */
			      /* all fonts except cminch. The largest char  */
			      /* 'W' of the latter font fits in this buffer */
			      /* up to magstep4.                            */

#define NAMELENGTH 80         /* Maximum length of filenames                */
#define NAMESIZE 2000         /* Maximum size of textbuffer for all names   */
#define STACKSIZE 100         /* Maximum depth for several stacks           */
#define TERMLINELENGTH 80     /* Maximum line length for terminal input     */


/*==========================================================================*
 * Don't modify the following definitions under no circumstances !!!!!      *
 *==========================================================================*/

#define BLACKBYTE '\377'
#define CHAR_HEAD 16
#define FALSE 0
#define FIX 1048576.0                  /* units of 2**-20 */
#define FMASK 7
#define INFINITY 0x7FFFFFFF
#define MAGSTEPBASE 1.2
#define SCALE 75780587.52              /* 72.27*2**20 */
#define RESOLUTION 300.0
#define TRUE  1
#define WHITEBYTE '\0'

#define DTODP 2.40                     /* dots to decimal_points */ 


/* HP-Laserjet PCL language dependent macros */

#define CHAR_ID "\033*c%dE\033(s%dW"   /* specify char and download it      */
#define CLEAN_PRINTER "\033E\033&k2G"  /* printer reset and def. line term. */
#define DRAWRULE "\033*c%db%da0P"      /* set rule a*b and draw it          */
#define DRAWROW "\033*b%dW"            /* draw one row of pixel             */
#define EJECT_PAGE " \f"               /* eject one page                    */
#define END_RASTER "\033*rB"           /* end raster graphic mode           */
#define FONT_ID "\033*c%dD"            /* specify font ID                   */
#define FONT_DESCR "\033)s64W"         /* declare font descriptor           */
#define GRAFRES "\033*t%sR"            /* set graphic resolution            */
#define INIT_PRINTER "\033E\033*t300R\033&l%dX"  /* printer initialization =
			   reset, graphic resolution = 300 dpi, # of copies */
#define LANDSCAPE "\033&l1O"           /* select landscape mode             */
#define MAKE_FONT_TEMP "\033*c4F"      /* make font temporary (last ID)     */
#define MANUAL_FEED "\033&l%dH"        /* feed from manual feed slot        */
#define MOVE_POSITION "\033&a%.2fh%.2fV" /* move cursor h and v decipoints  */
#define MOVE_H_POSITION "\033&a%.2fH"  /* move cursor h decipoints          */
#define MOVE_V_POSITION "\033&a%.2fV"  /* move cursor v decipoints          */
#define MOVE_REL "\033*p%+dx%+d%dY"    /* move cursor h and v dots relative */
#define MOVE_HREL "\033&a%+.2fH"       /* move cursor h decipoints relative */
#define MOVE_VREL "\033&a%+.2fV"       /* move cursor v decipoints relative */
#define PORTRAIT "\033&l0O"            /* select protrait mode              */
#define PUSH "\033&f0S"                /* push cursor position onto stack   */
#define POP  "\033&f1S"                /* pop cursor position from stack    */
#define SELECT_FONT "\033(%dX"         /* select font identifier            */
#define START_RASTER "\033*r1A"        /* start graphic mode                */
#define TRANSP_MODE "\033&p1X%c"       /* switch to transparent mode        */
#define TWOSIDED "\033&l%dS"           /* simplex/duplex print              */

#ifdef IID  
#define ENVELOPE "\033&l6H"            /* feed envelope from envelope feeder*/
				       /* customize this evtl. to 3 for
				       /* manual input or those tray, which */
				       /* holds the envelope cassette!      */
#define LETTERTRAY "\033&l4H"          /* feed paper from lower tray        */
#define BACKSIDE  "\033&a2G"           /* print backside only in duplex mode*/ 
#define FRONTSIDE "\033&a1G"           /* print frontside only in duplex md.*/
#endif



/*===========================================================================*
 *               global variables in alphabetical order                      *
 *===========================================================================*/

  int      actualpagecount;
  double   atof();
  char     bitname[NAMELENGTH];
  cdfmt    cd;                                   /* character descriptor  ## */
  charfmt *cbase;
  double   conv;
  double   convdot;
  double   convpxl;
  double   convtfm;
  long     count[10];
  char     curname[NAMELENGTH];
  int      c_black;
  int      c_flag;
  int      c_height;
  int      c_hoffset;
  int      c_nyb;
  byte     c_pad[] = {'\0','\200','\300','\340','\360','\370','\374','\376'};
  int      c_voffset;
  int      c_width;
  long     denominator;
  char     dviname[NAMELENGTH];
  charfmt *drawchar();
  int      dyn_f;
  byte    *endofchardata;
  int      e_offset;
  char     fatal_error[] = "\n This was a fatal error.\n";
  fdfmt    fd;                                   /* font descriptor       ## */
  long     firstpar();
  fontfmt *font;
  fontfmt *fontptr[MAXFONTS];
  gfontfmt gfont;
  gfontfmt gfontptr[MAXFONTS];
  int      fontpathlen;
  char    *graflist[] = {"300","150","100","75"};
  int      inpostamble;
  int      h_offset;
  int      h_posed;
  int      knownsize[] = {1,150,180,210,240,270,300,329,360,394,432,473,518,
		    568,622,681,746,818,896,981,1075,1178,1290,1413,1548,
                    1858,2035,2229,2442,2675,10000};
  int      landscape;
  long     lasmem;
  long     lastpage;
  int      lettertray;
  long     magnification;
  double   mag_printer;
  int      manualfeed;
  void    *malloc();
  int      maxdown;
  long     maxh;
  int      maxs;
  long     maxv;
  long     maxhsofar;
  int      maxssofar;
  long     maxvsofar;
  int      maxpages;
  char     names[NAMESIZE];
  int      nextfontdown;
  char    *nextnamesfree;
  int      numcopies;
  long     numerator;
  int      o_offset;
  int      outmode;
  int      perm_fonts_down;
  byte    *pkbase;
  long     pkbuffer[MAXPKSIZE];
  byte    *pkcharinfo();
  char    *pkname;
  char     pkfixname[NAMELENGTH];
  char     pkvarname[NAMELENGTH];
  byte    *pk_ptr;
  double   pow();
  long     power[8];
  int      prescan;
  long     prevpage;
  byte     pxlbuffer[MAXPXLSIZE];
  int      repeat_count;
  int      reversal;
  int      r_offset;
  byte    *rotate_char();
  int      s;                                             /* stack index */
  long     startcount[10];
  long     startloc;
  int      startthere[10];
  int      startvals;
  tfmfmt  *tfm;
  long    *tfmbase;
  char     tfmbuf[2048];
  char     tfmname[NAMELENGTH];
  int      twosided;
  char     usgname[NAMELENGTH];
  int      v_offset;
  int      v_posed;

#ifdef IID
  currentpage;
  char    *spcllist[] = {"move","grafix","include","landscape","letter",
			 "envelope","endenvelope"};
#else
  char    *spcllist[] = {"move","grafix","include","landscape"};
#endif


/*============================================================================*
 * The definition of DVI files refer to six registers, (h,v,w,x,y,z),  which  *
 * hold integer values in DVI units. In practice, we also need registers hh   *
 * and vv, the pixel analogs of h and v, since it is not allways true that    *
 * hh = pixel_round(h) or vv = pixel_round(v).                                *
 * The stack of (h,w,w,x,y,z) values is represented by eight arrays called    *
 * hstack,...,zstack, hhstack and vvstack.                                    */
  
  long  h,v,w,x,y,z;
  int   hh,vv;
  long  hstack[STACKSIZE], vstack[STACKSIZE], wstack[STACKSIZE],
	xstack[STACKSIZE], ystack[STACKSIZE], zstack[STACKSIZE];
  int   hhstack[STACKSIZE], vvstack[STACKSIZE];
/*============================================================================*/




/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 ******************************************************************************
 * ===>> main(argc,argv)                                                      *
 ******************************************************************************/

main(argc,argv)
int argc;
char *argv[];
{ initialize();                                /* get things started properly */
  dialog(argc,argv);                           /* process all dvihp options   */
  prescan = TRUE;                              /* activate prescan cycle      */
  opendvifile();
/*-----------------------------------------------------------------------------
  -                          prescan dvi file                                 -
  ----------------------------------------------------------------------------*/
  preamble();                /* process the preamble                          */
  skip_pages();              /* pass over pages up to starting page           */
  if(!inpostamble)           /* if not in postamble, prescan up to maxpages   */
  { while(maxpages-- > 0)
    { scan_page();           /* prescan up to maxpages or postamble           */
      if(inpostamble)
      { postamble();         /* check pre-post agreement and prescan-postamble*/
        break;               /* font_def agreement                            */
      }
      if(!between_pages()) break;
    }
  }
  prescan = FALSE;
/*-----------------------------------------------------------------------------
  -                    download the defined fonts                             -
  ----------------------------------------------------------------------------*/
  openbitfile();
  fprintf(bitfile , INIT_PRINTER , numcopies);
  cachefonts();

/*-----------------------------------------------------------------------------
  -                       now do the main work                                -
  ----------------------------------------------------------------------------*/
#ifndef IID

  if(twosided)
  { do_even_pages();
    printf("Reload paper cassette with output pacakage\n");
    fputs(MANUAL_FEED,bitfile);
    do_odd_pages();
  }
  else
  { if(!reversal) do_all_pages();
    else          do_rev_pages();
  }
#else
  if(!twosided) 
  { if(!reversal) do_all_pages(); 
    else          do_rev_pages(); 
  }
  else do_duplex_pages();
#endif

  fprintf(bitfile , CLEAN_PRINTER);
  diagnostics();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/




/******************************************************************************
 * All of the procedures used in dvihp are listed in alphabetical rather than *
 * in logical order, except for the 'error and die' routines which are grouped*
 * together:  error(s), valerror(s,v), valvalerror(s,u,v),  stringerror(s,t)  *
 * This routines are collected under the group name 'error and die', which is *
 * found on its alphabetical place in the following list.                     *
 ******************************************************************************/



/******************************************************************************
 * ===>>  between_pages()                                                     *
 * This routine processes the dvi file between the occurence of an 'eop' and  *
 * the next 'bop'                                                             *
 ******************************************************************************/
 between_pages()
 { int  k;
   long p;
   do
   { k = getbyte();
     if((k >= fnt_def1) && (k <= fnt_def4))
     { p = firstpar(k);
       skip_font_def();
       k = nop;
     }
   } while(k == nop);
   if(k == post)
   { inpostamble = TRUE;
     if(prescan) postamble();
     return(FALSE);
   }
   if(k != bop) valvalerror("Bad DVI file: byte %d is %d, not bop !\n",
			     ftell(dvifile) - 1 , k);
   scanbop();
   return(TRUE);
 }  



/****************************************************************************** 
 * ===>>  cachefonts()                                                        *
 * This is the procedure with the caching font responsibility. On entrance to *
 * this procedure, all the font usage information will have been loaded into  *
 * 'use_count' entries for each font. 'cachefonts' first checks which of the  *
 * fonts, used in the dvi-file, are permanently downloaded by calling 'perm_  *
 * fonts' which in turn returns the number of permanent downloaded fonts.     *
 * The remaining fonts are ordered in 'fontlist' in respect to their usage by *
 * calling 'sort_fonts' which in turn returns the number of remaining fonts   *
 * used in the document. These fonts will be downloaded in order of their     *
 * usage, those most heavily used comes first and with just the characters    *
 * used from these fonts. The downloading process continues so long the       *
 * printer's memory will allow more and the actual number of downloade fonts  *
 * doesn't exceed MAXDOWN. If there are fonts which can't be downloade any-   *
 * more, their character's pixel patterns together with a small font and      *
 * character overhead are stored dynamically in memory.                       *
 ******************************************************************************/
cachefonts()
{
  register int  f,n;
  int      act_fonts_down;
  int      fontnum;
  int      fontlist[MAXFONTS];
  
             /* Find all permanent downloaded fonts for dvifile in progress */
  act_fonts_down = perm_fonts_down = perm_fonts(); 

                         /* Sort additional fonts in respect to their usage */
  fontnum = sort_fonts(fontlist);

/*----------------------------------------------------------------------------
  -   decide for downloading or storage of fonts and characters and do it    -
  ---------------------------------------------------------------------------*/

  for(n = 0 ; n < fontnum ; n++)
  { f = fontlist[n];
    font =fontptr[f];
    cbase = font->chr;
    loadpkfile();
    makepkdir();
    convtfm = font->scaled_size / FIX ;
    convpxl = font->design_size / 65536.0 * font->dir_size / SCALE ;
    if(act_fonts_down >= maxdown || lasmem < MINLASMEM ) storefont(f); 
    else 
    { lasmem -= downloadfont(f); 
      act_fonts_down++;
    }
  }

  if(landscape)  fputs(LANDSCAPE,bitfile);
  if(twosided)   fprintf(bitfile, TWOSIDED, twosided);
  if(manualfeed) fprintf(bitfile, MANUAL_FEED,manualfeed);
  if(lettertray) fputs(LETTERTRAY,bitfile);
}



/******************************************************************************
 * ===>>  checkfont(f)                                                        *
 * Check that all font definitions found in the postamble agree with all the  *
 * font definitions found during prescan. For any disagreement a warning will *
 * be printed. Those fonts which are not detected during the prescan phase    *
 * will be defined now                                                        *
 ******************************************************************************/
checkfont(f)
int f;
{ register int i;
  int a,l;
  fontfmt *font;
  char *p;
  char  buffer[TERMLINELENGTH];

  if(f >= MAXFONTS)
    printf("Warning: only font definitions 0..255 are valid in dvihp\n");
  else if(!(font = fontptr[f]))  definefont(f);
  else
  { if(signedquad() != font->checksum)
      printf("Warning: pre-post disagreement in checksum for font # %d !\n",f);
    if(signedquad() != font->scaled_size)
      printf("Warning: pre-post disagreement in scaled_size for font # %d !\n",
              f);
    if(signedquad() != font->design_size)
      printf("Warning: pre-post disagreement in design_size for font # %d !\n",
              f);
    a = getbyte();
    l = getbyte();
    for(i = 0; i < a ; i++) buffer[i] = getbyte();
    buffer[a] = '\0';
    p = names + font->name;
    if(strcmp(p,buffer))
      printf("Warning: pre-post disagreement in path_name for font # %d !\n",
              f);
    for(i = 0; i < l ; i++) buffer[i] = getbyte();
    buffer[l] = '\0';
    p = names + font->name + a + 1;
    if(strcmp(p,buffer))
      printf("Warning: pre-post disagreement in font_name for font # %d !\n",
              f);
  }
}
    

/****************************************************************************** 
 * ===>>  definefont(f)                                                       *
 * This subroutine does the necessary things when a 'fnt_def' command is be-  *
 * ing processed.                                                             *
 ******************************************************************************/
definefont(f)
int f;
{ int a,l;                     /* font definition parameters */
  fontfmt *safefont;
  charfmt *u;
  int *known;
  int idealmag;
  register int k;
  double s;
  double _f;
  if(!inpostamble)
  { if(f >= MAXFONTS)
      error("only font definitions 0..255 are valid in dvihp\n");
    if(fontptr[f] != 0) valerror("Font number %d already defined!\n", f);
    safefont = font;
    font = (fontfmt *) malloc(sizeof(*font));
    fontptr[f] = font;
    font->checksum = signedquad();
    font->scaled_size = signedquad();
    font->design_size = signedquad();
    a = getbyte();
    l = getbyte();
    if(nextnamesfree - names + a + l +2 > NAMESIZE)
      valerror("dvihp capacity exceded (name size=%d)!\n",NAMESIZE);
    font->name = nextnamesfree - names;
    if(outmode > 0) printf("Font %d: ", f);
    for(k=0; k<a; k++) *nextnamesfree++ = getbyte();
    *nextnamesfree++ = '\0';
    for(k=0; k<l; k++) *nextnamesfree++ = getbyte();
    *nextnamesfree++ = '\0';
    u = font->chr;
    for(k=0; k<=255; k++)
    { u->use_count = 0;
      u++;
    }
    font->space = font->design_size / 6;
    font->down  = -3;
    if(outmode > 0) printfont();
    idealmag = round(font->scaled_size / font->design_size 
                     * magnification * RESOLUTION / 1000.0 * mag_printer);
    if((idealmag < 10) || (idealmag > 9999))
      valerror("Are you sure you want this font at %d ?\n", idealmag);
    known = knownsize;
    while(*known < idealmag) known++;
    if(((double) *known / idealmag) > ((double) idealmag / *(known-1)))
      known--;
    s = (double) *known / idealmag;
    if((s > 1.02) || (s < 0.98))
      printf("Warning---size %d does not exist; substituting %d.\n",
             idealmag, *known);
    font->dir_size = *known;
    font = safefont;
  } 
}



/******************************************************************************
 * ===>>  diagnostics()                                                       *
 * This procedure is included for debugging and monitoring purposes. It prints*
 * out memory usage if the debug option was chosen. It also creates an font-  *
 * usage file 'filname.usg' that contains the font->use_count table for each  *
 * font, used in the document.                                                *
 ******************************************************************************/
diagnostics()
{ register int i;
  int j,n;
  char *p;
  if(outmode)
  { printf("Number of pages processed: %d\n" , actualpagecount);
    printf("String pool usage: %d out of %d bytes\n" ,
            nextnamesfree - names , NAMESIZE);
    n = 0;
    openusgfile();
    for(i = 0 ; i < MAXFONTS ; i++)
      if(font = fontptr[i])
      { n++;
	p = names + font->name;
	if(*p) fprintf(usgfile,"%s:",p);
	while(*p++);
	fputs(p , usgfile);
	cbase = font->chr;
	for(j = 0 ; j < 256 ; j++) putw((cbase +j)->use_count,usgfile);
      }
    fclose(usgfile);
	 
    printf("Font description information: %d bytes\n" , 1564*n);
    printf("Laserjet memory available:    %d bytes\n" , lasmem);
  }
}



/******************************************************************************
 * ===>>  dialog(argc,argv)                                                   *
 * The selected options are put into global variables by this procedure, which*
 * is called just as dvihp begins.                                            *
 ******************************************************************************/
dialog(argc,argv)
int argc;
char *argv[];
{ int k;
  char *arg;
  char filename[NAMELENGTH];
  char *bufptr;
  outmode = 0;
  filename[0] = 0;
  while (--argc > 0)
  { arg = *++argv;
    if(arg[0] != '-') strcpy(filename,arg);
    else switch(arg[1])
    { case 's':                                            /* set start page */
        startvals = 0;
        startthere[0] = FALSE;
        bufptr = arg+3;
        k = 0;
        if(*bufptr != ' ')
        { do
          { if(*bufptr == '*')
            { startthere[k] = FALSE;
              bufptr++;
            }
            else
            { startthere[k] = TRUE;
              startcount[k] = getcount(bufptr);
            }
            if((k<9) && (*bufptr == '.'))
            { k++;
              bufptr++;
            }
            else if((*bufptr == ' ') || (*bufptr == '\0'))
              startvals = k;
            else
            { startvals = 0;
              k = 0;
            }
          } while(startvals != k);
        }
        break;
      case 'p':                         /* set number of pages to be printed */
        if((maxpages = getcount(arg+3)) <= 0) maxpages = MAXPAGES;
        break;
      case 'c':                                 /* set number of copies/page */
        if((numcopies = getcount(arg+3)) <= 0) numcopies = 1;
      break;
      case 'f':                  /* set max number of fonts to be downloaded */
	if((maxdown = getcount(arg+3)) <= 0 ) maxdown = MAXDOWN;
	break;
      case 'h':         /* set left offset (for odd pages in two-sided mode) */
	h_offset = dim_to_dots(arg+3);
	break;
      case 'r':          /* set left offset for even pages in two-sided mode */
	r_offset = dim_to_dots(arg+3);
	break;
      case 'v':                                   /* set vertical top offset */
	v_offset = dim_to_dots(arg+3);
	break;
      case 'x':                                   /* magnify (eXtend) output */
	mag_printer = atof(arg+3);                /* by given float value    */
	break;
      case 'e':                                   /* magnify (Enlarge) output*/
	mag_printer = pow(MAGSTEPBASE,atof(arg+3));    /* by given magstep   */
	break;
      case 'l':                                   /* set landscape mode flag */
	landscape = TRUE;
	break;
      case 'm':                                      /* set manual feed flag */
#ifdef IID
	if(arg[2] == '=') manualfeed = getcount(arg+3);
	else              manualfeed = 2;
	if(manualfeed > 6 || manualfeed < 0)
	{ printf("Only 0 ... 6 are valid values for `manual' option\n");
	  printf("Option will be ignored!!!\n");
	  manualfeed = FALSE;
        }
#else   
	manualfeed = TRUE;
#endif
	break;
      case 'o':                  /* print pages in reversal (Opposite) order */
	reversal = TRUE;
	break;
      case 't':                                 /* set 'two-sided' mode flag */
#ifdef IID
	if(arg[2] == '=') twosided = getcount(arg+3);
	else              twosided = TRUE;            /* set to 1 as default */
 	if(twosided > 2 || twosided < 0)
	{ printf("Only 0, 1, 2 are valid values for `twosided' option\n");
	  printf("Default value `twosided=1' will be used!!!\n");
	  twosided = TRUE;
	}   
#else
	twosided = TRUE;
#endif
	break;
      case 'd':                                     /* set 'debug' mode flag */
        outmode = TRUE;
      break;
      default:
        printf("Unknown option : %s\n",arg);
    }
  }

  h_offset = round(h_offset/mag_printer);       /* Make offsets independent  */
  r_offset = round(r_offset/mag_printer);       /* from the chosen magnifi-  */
  v_offset = round(v_offset/mag_printer);       /* cation! Offsets should be */
						/* absolute!                 */

  if(twosided)
  {
#ifndef IID  
    if(!startvals && !(startcount[0] % 2))
    { if(startthere[0]) startcount[0]--;
      else
      { startthere[0] = TRUE;
	startcount[0] = 1;
      }
    }
    if(maxpages % 2) maxpages++;
#else
  currentpage = startcount[0];
#endif
    o_offset = h_offset;
    e_offset = r_offset;
  }
   
   
  if(outmode)
  { printf("Options selected:\n");
    printf("  Starting page = ");
    for(k=0; k<=startvals; k++)
    { if(startthere[k]) printf("%d",startcount[k]);
      else printf("*");
      if(k<startvals) printf(".");
      else printf("\n");
    }
    printf("  Maximum numbers of pages =%d\n",maxpages);
    printf("  Number of copies = %d\n",numcopies);
    if(twosided) printf("  Two-sided printing\n");
    if(manualfeed) printf("  Feed pages maually\n");
  }
  if(!filename[0]) error("No file name given on command line!\n");
  strcpy(dviname,filename);
  strcpy(bitname,filename);
  strcat(dviname,".dvi");
  strcat(bitname,".bit");
  if(outmode)
  { strcpy(usgname,filename);
    strcat(usgname,".usg");
  }
}



/******************************************************************************
 * ===> dim_to_dots(data)                                                     *
 * This routine converts a string which contains a floating number terminated *
 * by a dimension into the proper number of quarterdots for 300 dots/inch.    *
 ******************************************************************************/
dim_to_dots(data)
char *data;
{ float val;
  int  ival;
  char  dim;
  char  dimstr[4];
  double _f;
  sscanf(data,"%f%c",&val,&dim);
  switch(dim)
  { case 'i':
      ival = round(300.0 * val);
      break;
    case 'c':
      ival = round(300.0/2.54 * val);
      break;
    case 'm':
      ival = round(300.0/25.4 * val);
      break;
    case 'p':
      ival = round(300.0/72.27 * val);
      break;
    case 'd':
      ival = round(val);
      break;
    default:
      sscanf(data,"%*f%3s",dimstr);
      printf("Unknown dimension '%3s' in dialog or special\n",dimstr);
      ival = 300;
      break;
  }
  return(ival);
}


      

/******************************************************************************
 * ===>  do_all_pages()                                                       *
 * This routine processes all pages in its natural order in face down mode.   *
 ******************************************************************************/
do_all_pages()
{ fseek(dvifile, startloc, 0);
  inpostamble = FALSE;
  maxpages = actualpagecount;
  while(maxpages-- > 0) if(between_pages()) { printbop() ; dopage(); }
}


#ifdef IID

/******************************************************************************
 * ===>  do_duplex_pages()                                                    *
 * This routine processes all pages in duplex mode. If the first page number  *
 * is even, only the backside will be printed. As well, if the last page num- *
 * ber is odd, only the frontside will be printed. All other pages are printed*
 * on both sides and the output occures in natural order.
 ******************************************************************************/
do_duplex_pages()
{ fseek(dvifile, startloc, 0);
  inpostamble = FALSE;
  maxpages = actualpagecount;
  if(!(currentpage % 2)) fputs(BACKSIDE, bitfile);
  while(maxpages-- > 0)
  { if(currentpage++ % 2) h_offset = o_offset;
    else                  h_offset = e_offset;
    if(between_pages()) { printbop() ; dopage(); }
  }
  if(!(currentpage % 2)) fputs(FRONTSIDE, bitfile);
}

#else

/******************************************************************************
 * ===>  do_even_pages()                                                      *
 * This routine processes all even pages in the 'two-sided' mode. This proce- *
 * dure is actually the first path of the two sided mode in which page proces-*
 * sing is done in reversal order from higher to lower page numbers. By this  *
 * way the Laserjet II printer outputs the package of even pages in reversal  *
 * order.   After loading the paper cassette with this package again - or by  *
 * manual feeding - the final output from the second path occurs in natural   *
 * order.                                                                     *
 ******************************************************************************/
do_even_pages()
{ long backpage;
  h_offset = r_offset;
  if(inpostamble) backpage = lastpage + 1;
  else            backpage = prevpage + 1;
  maxpages = actualpagecount;
  while(maxpages--)
  { fseek(dvifile , backpage , 0);
    scanbop();
    printbop();
    backpage = prevpage + 1;
    dopage();
    if(maxpages > 0)
    { fseek(dvifile , backpage , 0);
      scanbop();
      backpage = prevpage + 1;
      maxpages--;
    }
  }
}



/******************************************************************************
 * ===>  do_odd_pages()                                                       *
 * This routine processes all odd pages in the 'two-sided' mode. This proce-  *
 * dure is actually the second path of the two sided mode in which page pro-  *
 * cessing is done in natural order from lower to higher page numbers. By     *
 * this way the Laserjet II outputs the whole document finally in natural     *
 * order!                                                                     *
 ******************************************************************************/
do_odd_pages()
{ fseek(dvifile , startloc , 0);
  inpostamble = FALSE;
  h_offset = o_offset;
  maxpages = actualpagecount;
  while(maxpages-- > 0)
  { if(between_pages())
    { printbop();
      dopage();
      maxpages--;
      skip_next_page();
    }
    else
    { fputs(EJECT_PAGE,bitfile);
      break;
    }
  }
}

#endif



/******************************************************************************
 * ===>  do_rev_pages()                                                       *
 * This routine processes all pages in reversal order in face up mode.        *
 ******************************************************************************/
do_rev_pages()
{ long backpage;
  if(inpostamble) backpage = lastpage + 1;
  else            backpage = prevpage + 1;
  maxpages = actualpagecount;
  while(maxpages-- > 0)
  { fseek(dvifile , backpage , 0);
    scanbop();
    backpage = prevpage + 1;
    printbop();
    dopage();
  }
}


/******************************************************************************
 * ===>  dopage()                                                             *
 * This is the main printer dependent output routine, which is organized as   *
 * a function with side effects. The subroutine is organized as a typical in- *
 * terpreter, with a multiway branch of the command code followed by 'goto'   *
 * statements leading to branches that finish up the activities common to dif-*
 * ferent commands.                                                           *
 * dopage aborts 'dvihp' if somewhat unusual happens !!                       * 
 ******************************************************************************/
dopage()
{ register int o, _a;
  int hhh,vvv;
  register int k;
  int badchar;
  register long p,q;
  charfmt *c;
  double _f;

  h_posed = v_posed = FALSE;
  s = h = v = w = x = y = z = hh = vv = 0;
  font = NULL;
  while (TRUE)
  { o = getbyte();
    if(o > 127) p = firstpar(o);
    if(o < 128)
    { c = drawchar(o);
      q = c->tfm_width;
      hh += c->pxl_width;
      goto move_right;
    }
    else switch (o)
    { case set1:
      case set2:
      case set3:
      case set4:
        c = drawchar(p);
        q = c->tfm_width;
        hh += c->pxl_width;
        goto move_right;
        break;
      case put1:
      case put2:
      case put3:
      case put4:
        drawchar(p);
        goto done;
        break;
      case set_rule:
        q = signedquad();
        drawrule(rulepixels(p),rulepixels(q));
        hh += rulepixels(q);
        goto move_right;
        break;
      case put_rule:
        q = signedquad();
        drawrule(rulepixels(p),rulepixels(q));
        goto done;
        break;
      case nop:
        goto done;
        break;
      case bop:
        printf("bop occured before eop\n");
        goto endfalse;
        break;
      case eop:
        if(maxpages) putc ('\014',bitfile);
        if(s) printf("stack not empty at end of page!\n");
        return (TRUE);
        break;
      case push:
        if(s == maxssofar)
        { maxssofar++;
          if(s == maxs) printf("deeper than claimed in postamble\n");
          if(s == STACKSIZE)
          { printf("dvihp capacity exceeded STACKSIZE!\n");
            goto endfalse;
          }
        }
        hstack[s] = h;
        vstack[s] = v;
        wstack[s] = w;
        xstack[s] = x;
        ystack[s] = y;
        zstack[s] = z;
        hhstack[s] = hh;
        vvstack[s] = vv;
        s++;
        goto done;
        break;
      case pop:
        if(s)
        { s--;
          if(hh != hhstack[s]) h_posed = FALSE;
          if(vv != vvstack[s]) v_posed = FALSE;
          h = hstack[s];
          v = vstack[s];
          w = wstack[s];
          x = xstack[s];
          y = ystack[s];
          z = zstack[s];
          hh = hhstack[s];
          vv = vvstack[s];
        }
        else printf("(illegal at level zero)!\n");
        goto done;
        break;
      case right1:
      case right2:
      case right3:
      case right4:
        goto out_space;
        break;
      case w0:
      case w1:
      case w2:
      case w3:
      case w4:
        w = p;
        goto out_space;
        break;
      case x0:
      case x1:
      case x2:
      case x3:
      case x4:
        x = p;
        goto out_space;
        break;
      case down1:
      case down2:
      case down3:
      case down4:
        goto move_down;
        break;
      case y0:
      case y1:
      case y2:
      case y3:
      case y4:
        y = p;
        goto move_down;
        break;
      case z0:
      case z1:
      case z2:
      case z3:
      case z4:
        z = p;
        goto move_down;
        break;
      case fnt_def1:
      case fnt_def2:
      case fnt_def3:
      case fnt_def4:
        skip_font_def();
        goto done;
        break;
      case xxx1:
      case xxx2:
      case xxx3:
      case xxx4:
        badchar = FALSE;
        if(nextnamesfree - names + p > NAMESIZE )
          error("Out of string space during special!\n");
        for(k = 0 ; k < p ; k++)
        { q = getbyte();
          if((q < ' ') || (q > '~')) badchar = TRUE;
          *(nextnamesfree + k) = q;
        }
        *(nextnamesfree + p) = 0;
        if(badchar) printf("non-ASCII character in xxx command!\n");
        dospecial();
        goto done;
        break;
      case pre:
	printf("preamble command within a page !\n");
	goto endfalse;
	break;
      case post:
      case postpost:
        printf("postamble command within a page!\n");
        goto endfalse;
        break;
      case undef0:
      case undef1:
      case undef2:
      case undef3:
      case undef4:
      case undef5:
        printf("Undefined command %d\n",o);
        goto done;
        break;
      default:
        font = fontptr[p];
	gfont = gfontptr[p];
        if(font->down > 0 ) fprintf(bitfile , SELECT_FONT , font->down);
	goto done;
        break;
    }
       
    out_space:
      if((font == NULL) || (p >= font->space) || (p <= -4*font->space))
           hh = round(conv*(h+p));
      else hh += round(conv*p);
      q = p;
      h_posed = FALSE;
    move_right:
      hhh = round(conv*(h+q));
      if(abs(hhh - hh) > MAXDRIFT)
      { h_posed = FALSE;
	hh = hhh > hh ? hhh - MAXDRIFT : hhh + MAXDRIFT;
      }
      h += q;
      if(abs(h) > maxhsofar)
      { if(abs(h) > maxh + 99)
        { printf("Warning: |h| > %d\n", maxh);
          maxh = abs(h);
        }
        maxhsofar = abs(h);
      }
      goto done;
    
    move_down:
      if((font == NULL) || (abs(p) >= 5*font->space))
           vv = round(conv*(v+p));
      else vv += round(conv*p);
      v_posed = FALSE;
      if((v > 0) &&  (p > 0) && (v > INFINITY-p))
        printf("arithmetic overflow! parameter changed from %d to %d\n",
               p , p = INFINITY -v);
      if((v < 0) && (p < 0) && (-v > INFINITY+p))
        printf("arithmetic overflow! parameter changed from %d to %d\n",
               p , p = -(INFINITY+v));
      vvv = round(conv*(v+p));
      if(abs(vvv - vv) > MAXDRIFT)
	vv = vvv > vv ? vvv - MAXDRIFT : vvv + MAXDRIFT;
      v += p;
      if(abs(v) > maxvsofar)
      { if(abs(v) > maxv + 99)
        { printf("Warning: |v| > %d\n",maxv);
          maxv = abs(v);
        }
        maxvsofar = abs(v);
      }
      goto done;

    done:;
  }
  endfalse: error("\n! Bad DVI file: page ended enexpectedly !\n");
     
}



/******************************************************************************
 * ===>>  dospecial()                                                         *
 * This procedure will handle any special commands that might appear.         *
 * Commands must have the following format                                    *
 *       command(parameters ...)  or   command                                *
 * If there is more than one command in the special, they must be seperated   *
 * by spaces. The parameters are also sepereated by spaces. The string that   *
 * was contained in the special command is in the names array starting at     *
 * 'nextnamesfree' and ending with a \0. Also, the 'prescan' boolean should   *
 * be checked to see if this is the first or second time that this string was *
 * encountered.                                                               *
 ******************************************************************************/
dospecial()
{ char *bufptr;
  char *iptr,*jptr;
  char *parptr;
  char c;
  register int i,j;
  int  k,l;
  int  hoffset,voffset;
  int  pxl_rows,row_bytes;
  int  gflag;
  int  relative;
  char drawrow[8];
  int no;

  no = 1;
  /* ===> start */
  bufptr = nextnamesfree;

  while(*bufptr)
  { while(*bufptr == ' ') bufptr++;
    if(*bufptr)
    { k = 0;
      parptr = NULL;
      iptr = jptr = bufptr;
      while((*jptr != ' ') && (*jptr != '(') && *jptr) jptr++;
      if(*jptr == '(') parptr = jptr + 1;
      *jptr = '\0'; 
      while(k < 8)
	if(!strcmp(bufptr,spcllist[k])) break;
	else k++;

      bufptr = jptr;
      if(parptr)
      { bufptr++;
	while((*bufptr != ')') && *bufptr) bufptr++;
        if(*bufptr == 0)
          printf("Expected paranthesis after parameter in special!\n");
        else bufptr++;
      }

      switch (k)
      { case 0:                                     /* move(h=..dim v=..dim) */
	  if(!prescan)
	  { hoffset = voffset = 0;
	    relative = TRUE;
	    jptr = parptr;
	    while(*jptr != ')')
	    { while(*jptr == ' ') jptr++;
	      if(!(c = *jptr++));
	      { if(*jptr == '=') jptr++;
		switch(c)
		{ case 'h':
		    hoffset = dim_to_dots(jptr);
		    break;
		  case 'v':
		    voffset = dim_to_dots(jptr);
		    break;
		  case 'a':
		    relative = FALSE;
		    break;
		  case 'r':
		    relative = TRUE;
		    break;
		  default:
		    printf("Invalid paramter in 'move()' special\n");
		    break;
		}
	      }
	      while((*jptr != ' ') && (*jptr != ')') && *jptr) jptr++;
	    }
	    if(relative)
	    { if(hoffset) fprintf(bitfile , MOVE_HREL , hoffset * DTODP);
	      if(voffset) fprintf(bitfile , MOVE_VREL , voffset * DTODP);
	    }
	    else
	    { if(hoffset) fprintf(bitfile , MOVE_H_POSITION , hoffset * DTODP);
	      if(voffset) fprintf(bitfile , MOVE_V_POSITION , voffset * DTODP);
	    }
	  }
	  break;

        case 1:                              /* grafix(filename [resolution]) */
	  if(!prescan)
	  { jptr = parptr;
	    iptr = curname;
	    while((*jptr != ' ') && (*jptr != ')') && *jptr) *iptr++ = *jptr++;
	    *iptr = '\0';
	    openinputtext(curname);
	    while(*jptr == ' ') jptr++;
	    parptr = jptr;
	    while((*jptr != ' ') && (*jptr != ')') && *jptr) jptr++;
	    if(jptr != parptr)
	    { *jptr = '\0';
	      l = 0;
	      while(l < 4)
		if(!strcmp(parptr,graflist[l])) break;
		else l++;
	    }
	    else l = 1; 
	    if(l == 4)
	    { printf("Invalid resolution in grafix() special!\n");
	      l = 1;
	    }
	    if(l) fprintf(bitfile , GRAFRES , graflist[l]);
	    fputs(PUSH,bitfile);
	    fputs(START_RASTER , bitfile);
	    pxl_rows = getc(geninput);
	    pxl_rows = (pxl_rows << 8) + getc(geninput);
	    gflag = getc(geninput);
	    if(!gflag) sprintf(drawrow , DRAWROW , row_bytes = getc(geninput));
	    for(i = 0 ; i < pxl_rows ; i++)
	    { if(gflag) fprintf(bitfile , DRAWROW , row_bytes = getc(geninput));
	      else      fputs(drawrow , bitfile);
	      for(j = 0 ; j < row_bytes ; j++)
		putc(getc(geninput) , bitfile);
	    }
	    fputs(END_RASTER , bitfile);
	    if(l) fprintf(bitfile , GRAFRES , graflist[0]);
	    fputs(POP , bitfile);
	    fclose(geninput);
	  }
	  break;

	case 2:                                         /* include(filename) */
	  if(!prescan)
	  { jptr = parptr;
	    iptr = curname;
	    while((*jptr != ' ') && (*jptr != ')') && *jptr) *iptr++ = *jptr++;
	    *iptr = 0;
	    openinputtext(curname);
	    fputs(PUSH,bitfile);
	    while(!feof(geninput)) putc(getc(geninput) , bitfile);
	    fputs(POP,bitfile);
	  }
	  break;

        case 3:                                            /* landscape mode */
          if(prescan) landscape = TRUE;
          break;

#ifdef IID
        case 4:
	  if(prescan) lettertray = TRUE;
	  break;

	case 5:
	  if(!prescan) fputs(ENVELOPE,bitfile);
	  break;
	case 6:
	  if(!prescan) fputs(LETTERTRAY,bitfile);
	  break;

	case 7:
	  if(prescan)
	  { iptr = nextnamesfree;
            while(*iptr) putchar(*iptr++);
            printf("\nSpecial command not understood!\n");
	  }
          break;
#else

	case 4:
	  if(prescan)
	  { iptr = nextnamesfree;
            while(*iptr) putchar(*iptr++);
            printf("\nSpecial command not understood!\n");
	  }
          break;
#endif
      }
    }
  }
}
   


/******************************************************************************
 * ===>>  downloadfont()                                                      *
 ******************************************************************************/
downloadfont(f)
int f;
{ int i;
  int depth, max_depth, max_width, max_offset;
  int xheight;
  long memreq;

  max_depth = max_width = max_offset = 0;
  memreq = 64;

  if(outmode) printf("Downloading . . .\n");
/*-----------------------------------------------------------------------------
  -        specify font ID = nextfontdown and download each character         -
  -        of the current font which occur in dvifile                         -
  ----------------------------------------------------------------------------*/
  fprintf(bitfile , FONT_ID , font->down = nextfontdown--);
  for(i = font->bc ; i <= font->ec ; i++)
    if((cbase + i)->use_count)
    { pkcharinfo(i);
      if(c_voffset > max_offset) max_offset = c_voffset;
      if((depth = c_height - c_voffset) > max_depth) max_depth = depth;
      if(c_width > max_width) max_width = c_width;
    }
  pkcharinfo('x');   xheight = c_height;



/*-----------------------------------------------------------------------------
  -   Now determine font descriptor from max values above                     -
  ----------------------------------------------------------------------------*/
  fd.baseline    = max_offset;
  fd.cell_height = max_depth ? max_offset + max_depth : max_offset + 1;
  fd.cell_width  = max_width;
#ifdef IID
  fd.orientation = 0;
#else
  fd.orientation = landscape ? 1 : 0;
#endif
  fd.height      = 4 * font->height;
  fd.xheight     = 4 * xheight;
  fd.u_distance  = -max_depth;
  fd.text_height = 4 * fd.cell_height;
  fd.text_width  = 4 * fd.cell_width;
  fd.symbol_set  = (DYNAMICFONTBASE - nextfontdown + perm_fonts_down) * 32
		    + 'X' - 96;
  i = 0;
  while(i <= 15 && (fd.name[i] = pkvarname[i]) ) i++;

  fputs(FONT_DESCR, bitfile);
  fwrite((char *) &fd, sizeof(fd), 1, bitfile);
 

/*-----------------------------------------------------------------------------
  -   Now download all characters used from this font                         -
  ----------------------------------------------------------------------------*/
  for(i = font->bc ; i <= font->ec ; i++)
    if((cbase + i)->use_count) memreq += down_character(i);
  fprintf(bitfile, MAKE_FONT_TEMP);                   /* Make font temporaray */

  return(memreq);
}



/*****************************************************************************
 * ===>> down_character(c)                                                   *
 * download character 'c', assuming that the corresponding font file is in   *
 * .PK format. Therefore 'pktopxl(c)' is called, which converts the pk-coded *
 * character into the corresponding byte aligned pixel pattern, stored in    *
 * 'pxlbuffer'. 'pktopxl(c)' also determines the global variables            *
 *      c_width, c_height, c_hoffset, c_voffset.                             *
 * For landscape mode the pixel pattern then will be rotated and stored at   *
 * the end of the character pixel pattern at 'endofchardata' in pxlbuffer.   *
 * The appropriate pixel pattern then is downloaded, i.e is put in bitfile,  *
 * if possible, including the corresponding character descriptor for the     *
 * laserjet II.  For those characters which doesn't fit into laserjets max.  *
 * character cell (use_count < 0) raster information is stored in *gfont     *
 * and memory space is allocated to store the corresponding pixel pattern.   *
 * Stored characters are drawn in graphic mode by 'draw_character(c)'.       *
 *****************************************************************************/
down_character(c)
int c;
{ register int i,j;
  charfmt *u;
  int length;
  int width_bytes;
  int width,height;
  byte *p,*q;
  
  pktopxl(c);
#ifndef IID
  if(landscape)
  { p = rotate_char();
    width  = c_height;
    height = c_width;
    cd.left_offset = -c_voffset;
    cd.top_offset  = height - c_hoffset - 1;
    cd.orientation = '\1';
  }
  else
#endif
  { width  = c_width;
    height = c_height;
    cd.left_offset = - c_hoffset;
    cd.top_offset  = c_voffset;
    cd.orientation = '\0';
    p =  pxlbuffer;
  }
  width_bytes = (width + 7)/8;
  length = height*width_bytes;
  if(!length)
  { length = 1;             /* special case for empty char */
    if(!width) width = 1; 
    if(!height) { height = 1; cd.top_offset = height; }
  }

/*........................................................................
  .                   Now download pixel pattern for c                   .
  ........................................................................*/
  u = cbase + c;

  cd.char_width  = width;
  cd.char_height = height;
  cd.delta_x = 4 * u->pxl_width;          /* value is given in quarterdots */
  fprintf(bitfile , CHAR_ID , c , length+16);
  fwrite((char *) &cd , sizeof(cd) , 1 , bitfile);
  for(i = 0 ; i < length ; i++) putc(*p++,bitfile);

  return(height*((width + 7)/8) + CHAR_HEAD);
}


/*****************************************************************************
 * ===> charfmt drawchar(c)                                                  *
 * On entrace of this routine, the character to be drawn will be in 'c', the *
 * font in use is pointed by the pointer '*font'. The external name by which *
 * the font is known in 'font->down'. The routine is also responsible for    *
 * setting the printer to the correct location on the page  ('hh' and 'vv')  *
 *****************************************************************************/
charfmt *drawchar(c)
int  c;
{ register int i,j;
  int pxl_bytes;
  charfmt  *u;
  gcharfmt *g;
  byte *p;
  char drawrow[8];
  if(!h_posed && !v_posed)
  { fprintf(bitfile , MOVE_POSITION , (hh + h_offset) * DTODP ,
				      (vv + v_offset) * DTODP );
    h_posed = v_posed = TRUE;
  }
  else if(!h_posed)
  { fprintf(bitfile , MOVE_H_POSITION , (hh + h_offset) * DTODP);
    h_posed = TRUE;
  }
  else if(!v_posed)
  { fprintf(bitfile , MOVE_V_POSITION , (vv + v_offset) * DTODP);
    v_posed = TRUE;
  }

  u = font->chr + c;
  if(u->use_count > 0)                       /* print downloaded character */
    if(c > 27) putc(c , bitfile);
    else if(c==0 || c > 6 && c < 16 || c==27)
      fprintf(bitfile , TRANSP_MODE , c);
    else putc(c , bitfile);
  else                         /* draw graphic raster pattern of character */
  { g = &(*gfont)[c];
    p = g->pxl_pattern;
    fputs(PUSH,bitfile);
    fprintf(bitfile,MOVE_REL,g->x_offset,g->y_offset);
    fputs(START_RASTER,bitfile);
    pxl_bytes = g->pxl_bytes;
    sprintf(drawrow , DRAWROW , pxl_bytes);
    for(i = 0 ; i < g->pxl_rows ; i++)
    { fputs(drawrow,bitfile);
      for(j = 0 ; j < pxl_bytes ; j++) putc(*p++,bitfile);
    }
    fputs(END_RASTER,bitfile);
    fputs(POP,bitfile);
    fprintf(bitfile,MOVE_HREL,u->pxl_width * DTODP);
  }
  return (u);
}



/******************************************************************************
 * ===>>  drawrule(a , b)                                                     *
 * Similar this procedure draws a rule (a solid black rectangle) of a pixels  *
 * in height and b pixels in width. Positioning is again essential.           *
 * The Laserjet II makes this very easy.                                      *
 ******************************************************************************/
drawrule(a , b)
int a,b;
{ fprintf(bitfile , MOVE_V_POSITION , (vv + v_offset - (a-1)) * DTODP);
  v_posed = FALSE;
  if(!h_posed) fprintf(bitfile , MOVE_H_POSITION , (hh + h_offset) * DTODP);
  h_posed = FALSE;
  fprintf(bitfile , DRAWRULE , a , b);
}


   
/******************************************
 * error and die routines                 *
 * prints error messages and exits dvihp  *
 ******************************************/

error(s)
char *s;
{ printf(s);
  printf(fatal_error);
  exit();
}

valerror(s,v)
char *s;
int  v;
{ printf(s,v);
  printf(fatal_error);
  exit();
}

valvalerror(s,u,v)
char *s;
int u,v;
{ printf(s,u,v);
  printf(fatal_error);
  exit();
}

stringerror(s,t)
char *s,*t;
{ printf(s,t);
  printf(fatal_error);
  exit();
}



/******************************************************************************
 * ===>>  firstpar(k)                                                         *
 * This routine computes the first parameter of each dvi opcode               *
 ******************************************************************************/
long firstpar(k)
int k;
{ 
#ifdef MACROS
  register int _a;
#endif
  switch (k)
  { case set1:
    case put1:
    case fnt1:
    case xxx1:
    case fnt_def1:
      return(getbyte());
      break;
    case set2:
    case put2:
    case fnt2:
    case xxx2:
    case fnt_def2:
      return(getpair());
      break;
    case set3:
    case put3:
    case fnt3:
    case xxx3:
    case fnt_def3:
      return(gettrio());
      break;
    case right1:
    case w1:
    case x1:
    case down1:
    case y1:
    case z1:
      return(signedbyte());
      break;
    case right2:
    case w2:
    case x2:
    case down2:
    case y2:
    case z2:
      return(signedpair());
      break;
    case right3:
    case w3:
    case x3:
    case down3:
    case y3:
    case z3:
      return(signedtrio());
      break;
    case set4:
    case set_rule:
    case put4:
    case put_rule:
    case right4:
    case w4:
    case x4:
    case down4:
    case y4:
    case z4:
    case fnt4:
    case xxx4:
    case fnt_def4:
      return(signedquad());
      break;
    case nop:
    case bop:
    case eop:
    case push:
    case pop:
    case pre:
    case post:
    case postpost:
    case undef0:
    case undef1:
    case undef2:
    case undef3:
    case undef4:
    case undef5:
      return(0);
      break;
    case w0:
      return(w);
      break;
    case x0:
      return(x);
      break;
    case y0:
      return(y);
      break;
    case z0:
      return(z);
      break;
    default:
      if((k >= fnt_num_0) && (k <= fnt_num_63)) return(k - fnt_num_0);
      break;
  }
}
        


/******************************************************************************
 * ===>>  getcount(p)                                                         *
 * converts string to integer and returns the integer while p is updated to   *
 * the first non digit charcter in string *p                                  *
 ******************************************************************************/
getcount(p)
char *p;
{ int negative;
  int x;
  if(*p == '-')
  { negative = TRUE;
    p++;
  }
  else negative = FALSE;
  x = 0;
  while((*p >= '0') && (*p <= '9')) x = 10*x + *p++ - '0';
  return(negative ? -x : x);
}

 

/*********************************
 * ===>> initialize()            *
 * get all variable initialized  *
 *********************************/
initialize()
{ int i,j;
  printf(BANNER);
  for(i=0; i<MAXFONTS; i++)
  { fontptr[i] = NULL;
    gfontptr[i] = NULL;
  }
  pkbase = (byte *) pkbuffer;
  nextnamesfree = names + 1;
  outmode = 0;
  numcopies = 1;
  mag_printer = 1.0;
  maxpages = MAXPAGES;
  maxdown  = MAXDOWN;
  startvals = 0;
  startthere[0] = FALSE;
  inpostamble = FALSE;
  maxv = INFINITY - 99;
  maxh = INFINITY - 99;
  maxs = STACKSIZE + 1;
  maxvsofar = 0;
  maxhsofar = 0;
  maxssofar = 0;
  h_posed = FALSE;
  v_posed = FALSE;
  h_offset = HOFFSET;
  r_offset = HOFFSET; 
  v_offset = VOFFSET;

  lastpage = -1;
  prevpage = -1;
  actualpagecount = 0;
  nextfontdown = DYNAMICFONTBASE;
  lasmem = 0;
  landscape = FALSE;
  twosided  = FALSE;
  manualfeed = FALSE;
  reversal  = FALSE;
  lettertray = FALSE;

  tfm = (tfmfmt *) tfmbuf;
  tfmbase = (int *) tfmbuf;

  fd.size   = 64;
  fd.fmt    = 0;
  fd.type   = 2;
  fd.res1 = fd.res2 = fd.res3 = fd.res4 = fd.res5 = fd.res6 = 0;
  fd.spacing = 1;
  fd.pitch = 0;
  fd.wtype = 0;
  fd.style = 0;
  fd.stroke_weight = 0;
  fd.ltypeface = 3;
  fd.htypeface = 1;
  fd.u_height  = 3;
  fd.serif_style = 0;
  fd.ext_pitch = 0;
  fd.ext_height = 0;
  fd.cap_height = 0;

  cd.fmt   = 4;
  cd.cont  = 0;
  cd.size  = 14;
  cd.class = 1;
  cd.res   = 0;

  j = 1;
  for(i=0 ; i<8; i++)
  { power[i] = j;
    j <<= 1;
  }
  
  strcpy(pkfixname,fontpath);
  fontpathlen = strlen(pkfixname);

}




/******************************************************************************
 * ===>>  loadpkfile()                                                        *
 * Here is a procedure that reads the .PK file into 'pkbuffer'. Other than    *
 * insuring that the initial two bytes are 247 and 89 (the pk_pre command,    *
 * followed by the current .PK format identifier), it does no format checks.  *
 * If the corresponding .PK file doesn't fit into pkbuffer, 'dvihp' will be   *
 * aborted. In this case MAXPKSIZE has to be increased by an appropriate      *
 * amount and 'dvihp.c' has to be recompiled. At this state the choosen value *
 * of MAXPKSIZE will serve all existing .PK files but for future .PK font     *
 * files this maybe could happen.                                             *
 ******************************************************************************/
loadpkfile()
{ long *pkptr;
  byte *p;
  openpkfile();
  pkptr = pkbuffer;
  p = (byte *) pkptr;
  if(outmode) printf("Trying to load %s\n",pkname);
  *pkptr++ = getw(pkfile);
  if((getpkbyte(p) != pk_pre) || (getpkbyte(p) != 89))
    printf("---not loaded, PK file is bad\n");
  else
  { while(!feof(pkfile))
    { *pkptr++ =  getw(pkfile);
      if(pkptr - pkbuffer >= MAXPKSIZE)
        error("dvihp memory size exceeded on load of PK file!\n");
    }
    font->down = -2;
  }
  fclose(pkfile);
}




/******************************************************************************
 * ===>>  loadtfmfile()                                                       *
 * This procedure will extract information from the tfm file which is neces-  *
 * sary to know about printer resident fonts.                                 *
 ******************************************************************************/
loadtfmfile()
{ double x,z;
  double pxlconv;
  long *t;
  byte *w_index;
  int  *width;
  charfmt *c;
  register int i;
  double _f;

  t = tfmbase;
  opentfmfile();
  if(outmode) printf("Trying to load %s\n",tfmname);
  while(!feof(tfmfile)) *t++ = getw(tfmfile);
  fclose(tfmfile);
  if(font->checksum != tfm->cs)
    printf("Checksum in tfm file does not match that in dvi file!\n");
  z = font->scaled_size / FIX;
  pxlconv = font->design_size/65536.0 * font->dir_size / SCALE ;
  width = tfmbase + tfm->lh + tfm->ec - tfm->bc + 7; 
  w_index = tfmbuf + 4 * (tfm->lh + 6);
  c = font->chr;
  for(i = 0 ; i < tfm->bc ; i++)
  { c->tfm_width = 0;
    c->pxl_width = 0;
    c++;
  }
  for(i = tfm->bc; i <= tfm->ec; i++)
  { x = *(width + *w_index);
    c->tfm_width = x*z;
    c->pxl_width = round(x*pxlconv);
    c++;
    w_index += 4;
  }
  for(i = tfm->ec + 1 ; i <= 255; i++)
  { c->tfm_width = 0;
    c->pxl_width = 0;
    c++;
  }
}



/******************************************************************************
 * ===>> makepkdir()                                                          *
 * The coding scheme for the different characters in .PK files in general are *
 * not stored in ascending order of the ascii value of the character. Access  *
 * to an individual character would need a lot of searching within the  pk-   *
 * buffer. So it is wise, to make a directory for all of the characters with- *
 * in the pkfont which holds a pointer to the flag byte for each of the cha-  *
 * racters when the pkfont is loaded the first time. These pointers are rela- *
 * tive to 'pkbase' and stored into '(cbase + c)->pk_char' of the correspon-  *
 * ding 'font'.                                                               *
 ******************************************************************************/
makepkdir()
{ byte *p,*pc;
  int format;
  register int i,flag;
  long ds,hppp,vppp;
  unsigned cc,pl;
  double _f;
 
  for(i=0 ; i<256 ; i++) (cbase + i)->pk_char = 0;
  p = pkbase + *(pkbase + 2) + 3;
#ifndef MACROS
  ds = getpkquad(p);  p += 4;                    
  if(font->checksum != getpkquad(p))
    printf("Checksum in PK file %s does not match that in dvi file!\n",
	    pkname);    p += 4;
  hppp = getpkquad(p);  p += 4;
  vppp = getpkquad(p);  p += 4;          /* points now to first pk charcter */
#else
  ds = getpkquad(p);
  if(font->checksum != getpkquad(p))
    printf("Checksum in PK file %s does not match that in dvi file!\n",
	    pkname); 
  hppp = getpkquad(p);
  vppp = getpkquad(p);                   /* points now to first pk charcter */
#endif
  font->height = round(ds / FIX * vppp / 65536.0);
  while((flag = getpkbyte(p)) != pk_post)
  { if(flag < pk_cmd)
    { pc = p - 1;
      format = flag & FMASK;
      if(format < 4)
      { pl = getpkbyte(p);
	cc = getpkbyte(p);
	p += (format*256 + pl);
      }
      else if(format < 7)
#ifndef MACROS
      { pl = getpkpair(p);  p += 2;
	cc = getpkbyte(p);
	p += ((format-4)*65536 + pl);
      }
      else
      { pl = getpkquad(p);  p += 4;
	cc = getpkquad(p);  p += 4;
	p += pl;
      }
#else
      { pl = getpkpair(p); 
	cc = getpkbyte(p);
	p += ((format-4)*65536 + pl);
      }
      else
      { pl = getpkquad(p);
	cc = getpkquad(p); 
	p += pl;
      }
#endif
      if(cc < 256) (cbase + cc)->pk_char =  pc - pkbase;
    }
    else
    { switch(flag)
      { case pk_xxx1:
	  pl = getpkbyte(p);
	  break;
#ifndef MACROS
	case pk_xxx2:
	  pl = getpkpair(p);  p += 2;
	  break;
	case pk_xxx3:
	  pl = getpktrio(p);  p += 3;
	  break;
	case pk_xxx4:
	  pl = getpkquad(p);  p += 4;
	  break;
#else
	case pk_xxx2:
	  pl = getpkpair(p); 
	  break;
	case pk_xxx3:
	  pl = getpktrio(p);
	  break;
	case pk_xxx4:
	  pl = getpkquad(p);
	  break;
#endif
	case pk_yyy:
	  pl = 4;
	  break;
	case pk_pre:
	  error("pk_pre command within PK file %s\n",pkname);
	  break;
	default:
	  pl = 0;
	  break;
      }
      p += pl;
    }
  }
}


/*************************
 * file opening routines *
 *************************/
openbitfile()
{ bitfile = fopen(bitname,"w");
}

opendvifile()
{ if((dvifile = fopen(dviname,"r")) == NULL)
    error("DVI file does not exist!\n");
}

openinputtext(curname)
char curname[];
{ if((geninput = fopen(curname,"r")) == NULL)
    stringerror("Error opening %s\n",curname);
}
  
openpkfile()
{ char *fptr,*nptr;
  nptr = &names[font->name];
  if(!*nptr)
  { fptr = pkfixname + fontpathlen;
#ifndef SINGLEPATH
    sprintf(fptr,"%d/",font->dir_size);
    fptr += strlen(fptr);
#endif
    pkname = pkfixname;
  } 

  if(!*nptr) nptr++;
  fptr = pkvarname;
  while (*nptr) *fptr++ = *nptr++;
  sprintf(fptr,".%dpk",font->dir_size);
  if(!*nptr) strcat(pkname,pkvarname);
  else   pkname = pkvarname;

  if((pkfile = fopen(pkname,"r")) == NULL)
  { printf("PK file %s does not exist!\n",pkname);
    fclose(pkfile);
    printf("Substituting cmr10 at ten points . . .\n");
    pkfile = fopen(defaultpk,"r");
  }
}

openusgfile()
{ usgfile = fopen(usgname,"w");
}

opentfmfile()
{ char *fptr,*nptr,*pptr;
  nptr = &names[font->name];
  fptr = tfmname;
  pptr = tfmpath;
  if(*nptr) while(*nptr) *fptr++ = *nptr++;
  else      while(*pptr) *fptr++ = *pptr++;
  nptr++;
  sprintf(fptr,"%s.tfm",nptr);
  if((tfmfile = fopen(tfmname,"r")) == NULL)
  { printf("TFM file %s does not exist!\n",tfmname);
    fclose(tfmfile);
    printf("Susbstituting cmr10 at ten points . . .\n");
    tfmfile = fopen(defaulttfm,"r");
  }
}




/*****************************************************************************
 * ===>> perm_fonts()                                                        *
 * determines how many fonts are permanently downloaded and which of them    *
 * correspond to the fonts, used in the dvi-file. Those are marked in their  *
 * font->down by the identifier fdown. All font information for a permanent  *
 * downloaded font is taken from its .tfm file by calling `loadtfmfile'.     *
 *****************************************************************************/
perm_fonts()
{ int act_fonts_down = 0;
  int fdown, fsize;
  char buffer[TERMLINELENGTH], orient[2];
  char resname[NAMELENGTH];
  char *fontname;
  register int f;

  strcpy(resname,respath);
  strcat(resname,"hp.fnt");
  openinputtext(resname);
  while(fscanf(geninput,"%s %d %d %s", buffer, &fsize, &fdown, orient) != EOF)
    if(buffer[0] == 'M') lasmem = fsize;
    else
    { while(getc(geninput) >= ' ');
#ifdef IID
      if((orient[0] == 'L' ) || (orient[0] == 'P' ))
#else
      if((orient[0] == 'L' && landscape) || (orient[0] == 'P' && !landscape))
#endif
      { for (f=0; f<MAXFONTS; f++)
          if(font = fontptr[f])
          { if(font->dir_size == fsize)
            { fontname = names + font->name;
              if(!*fontname)
              { fontname++;
                if(!strcmp(fontname,buffer))
                { font->down = fdown;
                  loadtfmfile();
                }
              }
            }
          }
        act_fonts_down++;
      }
    }
  fclose(geninput);
  return(act_fonts_down);
}
  



/******************************************************************************
 * ===>> pkcharinfo(c)                                                        *
 * These routine determines the global variables                              * 
 *    c_flag, dyn_f, c_width, c_height, c_hoffset, c_voffset                  * 
 * for the character c. (cbase + c)->pk_char points to its flag byte relative *
 * to pkbase. It returns a pointer, which points to the first run count of    *
 * the character in progress.                                                 *
 ******************************************************************************/
byte *pkcharinfo(c)
int c;
{ int format;
  byte *p;
  charfmt *u;
  long tfm;

  u = cbase + c;
  if(u->pk_char == 0)
  { c_flag = dyn_f = c_width = c_height = c_hoffset = c_voffset = 0;
    if(outmode) printf("Character %d does'nt exist in this font\n",c);
    return(NULL);
  }
  p = pkbase + u->pk_char;
  c_flag = getpkbyte(p);
  dyn_f = c_flag >> 4;
  format = c_flag & FMASK;
  if(format < 4)
  { p += 2;
    tfm = getpktrio(p);
#ifndef MACROS
    p += 4;
#else
    p++;
#endif
    c_width = getpkbyte(p);
    c_height = getpkbyte(p);
    c_hoffset = sigpkbyte(p);
    c_voffset = sigpkbyte(p);
  }
  else if(format < 7)
  { p += 3;
#ifndef MACROS
    tfm = getpktrio(p);        p += 5;
    c_width = getpkpair(p);    p += 2;
    c_height = getpkpair(p);   p += 2;
    c_hoffset = sigpkpair(p);  p += 2;
    c_voffset = sigpkpair(p);  p += 2;
#else
    tfm = getpktrio(p);        p += 2;
    c_width = getpkpair(p);   
    c_height = getpkpair(p); 
    c_hoffset = sigpkpair(p); 
    c_voffset = sigpkpair(p);
#endif
  }
  else
  { p += 8;
#ifndef MACROS
    tfm = getpkquad(p);        p += 12;
    c_width = getpkquad(p);    p += 4;
    c_height = getpkquad(p);   p + =4;
    c_hoffset = sigpkquad(p);  p += 4;
    c_voffset = sigpkquad(p);  p += 4;
#else
    tfm = getpkquad(p);        p += 8;
    c_width = getpkquad(p);   
    c_height = getpkquad(p); 
    c_hoffset = sigpkquad(p); 
    c_voffset = sigpkquad(p);
#endif
  }
  u->tfm_width = round(tfm*convtfm);
  u->pxl_width = round(tfm*convpxl);
  return(p);
}



/******************************************************************************
 * ===>> pk_num()                                                             *
 * This routine deliveres the final algorithm for decoding the run counts,    *
 * assuming a procedure (macro) 'get_nyb()' is available to get the next      *
 * nybble from the 'pkbuffer'. It returns the next run_count as an integer,   *
 * and if the row needs to be repeated it returns additionally the repeat     *
 * value to the global variable 'repeat_count'. Since a repeat count can      *
 * occure only once within a row, namely after the first transition within a  *
 * row, it is the responsibility of the calling procedure to repeat such a    *
 * row and to reset 'repeat_count' to zero, before calling the next run count *
 * for the next row. - The global variable 'c_black' changes from 0 to -1 and *
 * back to 0 each time when 'pk_num' is called, indicating a run count for    *
 * black pixels by 'c_black = -1' or for white pixels by 'c_black' = 0, resp. *
 * Note that this routine is recursive, but since a repeat count can never    *
 * directly follow another repeat count, it can only be recursive to one level*
 ******************************************************************************/
pk_num()
{ register int i,j;
  c_black = ~c_black;
  if(!(i  = get_nyb()))
  { do i++; while(!(j = get_nyb()));
    while(i--) j = j*16 + get_nyb();
    return(j - 15 + (13 - dyn_f)*16 + dyn_f);
  }
  else if(i <= dyn_f) return(i);
  else if(i < 14) return((i - dyn_f - 1)*16 + get_nyb() + dyn_f + 1);
  else
  { if(repeat_count) error("Extra repeat count\n");
    if(i == 14) repeat_count = pk_num();
    else       {repeat_count = 1; c_black = ~c_black;}
    return(pk_num());
  }
}

 

/******************************************************************************
 * ===>> pktopxl(c)                                                           *
 * This routine converts a character coded in PK format into the appropriate  *
 * byte aligned pixel pattern. After calling 'pkcharinfo(c)', besides the glo-*
 * bal variables 'dyn_f, c_flag, c_width, c_height, c_hoffset and c_voffset'  *
 * a pointer to the first run count is returned and stored in the global var  *
 * 'pk_ptr'. The produced pixel pattern is stored in the global array 'pxl-   *
 * buffer', and the global pointer 'endofchardata' points to the next free    *
 * byte in 'pxlbuffer.                                                        *
 *----------------------------------------------------------------------------*
 * 'pktopxl' has two main branches: for those characters which are coded into *
 * run-repeat counts, indicated by a value of dyn_f < 14, the first branch is *
 * taken, while for bit mapped characters, indicated by dyn_f == 14, the      *
 * second branch is taken.                                                    *
 ******************************************************************************/
pktopxl(c)
int c;
{ byte *pxl,*row,*p;
  byte pxl_byte;
  int pad_bits,pad_comp;
  int pad_mask;
  int width_bytes;
  int nrows,length;
  int l,m,n;
  int run_count;

  pk_ptr = pkcharinfo(c);
  nrows = c_height;
  width_bytes = (c_width + 7) / 8;
  pad_bits = c_width % 8;
  pad_mask = c_pad[pad_bits];
  pxl = pxlbuffer;

  if(dyn_f < 14)
  {/*....................................................................
    .  convert run counts/repeat counts to byte aligned pixel pattern   .
    .....................................................................*/
    run_count = repeat_count = 0;
    c_nyb = 0;
    c_black = (c_flag & 0x8) ? 0 : ~0; 
    while(nrows)
    { if(repeat_count)
      { while(repeat_count)
        { repeat_count--;
  	  m = width_bytes;
          while(m--) *pxl++ = *row++;
          nrows--;
        }
      }
      else
      { if(!run_count) run_count = pk_num(); 
        if(run_count >= c_width)
          while(run_count >= c_width)
	  { run_count -= c_width;
	    m = pad_bits ? width_bytes : width_bytes + 1;
	    while(--m) *pxl++ = c_black ? BLACKBYTE : WHITEBYTE;
	    if(pad_bits) *pxl++ = c_black ? pad_mask : WHITEBYTE;
	    nrows--;
	  }
        else
        { row = pxl;
	  m = pad_bits ? width_bytes : width_bytes + 1;
	  while(--m)
	  { if(run_count < 8)
	    { pxl_byte = c_black ? c_pad[run_count] : ~c_pad[run_count];
	      while((run_count += pk_num()) < 8)
	        if(c_black) pxl_byte &=  c_pad[run_count];
	        else        pxl_byte |= ~c_pad[run_count];
	      *pxl++ = pxl_byte;
	    }
	    else *pxl++ = c_black ? BLACKBYTE : WHITEBYTE;
	    run_count -= 8;
	  }
	  if(pad_bits)
	  { if(run_count < pad_bits)
	    { pxl_byte = c_black ? c_pad[run_count] : ~c_pad[run_count];
	      while((run_count += pk_num()) < pad_bits)
	        if(c_black) pxl_byte &=  c_pad[run_count];
	        else        pxl_byte |= ~c_pad[run_count];
	      *pxl++ = pxl_byte & pad_mask; 
	    }
	    else *pxl++ = c_black ? pad_mask : WHITEBYTE;
	  }
	  nrows--;
	  run_count -= pad_bits;
        }
      }
    }
  }
  else
  {/*....................................................................
    .  convert bit mapped character into byte aligned pixel pattern     .
    .....................................................................*/
    p = pk_ptr;
    pad_comp = 8 - pad_bits;
    if(pad_bits)
    { m=0;
      while(nrows--)
      { l = width_bytes;
        while(--l) *pxl++ = m ? (*p++) << m | (*p >> n) & ~c_pad[n] : *p++;
        if(m) *pxl++ = m <= pad_comp ? (*p << m) & pad_mask 
	               : (*p++) << m | *p >> n & ~c_pad[n] & pad_mask;
        else  *pxl++ = *p & pad_mask;
        m = (m + c_width) % 8;
        if(!m) p++;
        else   n = 8 - m;
      }
    }
    else
    { length = width_bytes*c_height;
      if(!length) *pxl++ = 0;        /* special case for empty char */
      while(length--) *pxl++ = *p++;
    }
  }
  endofchardata = pxl;
}



/******************************************************************************
 * ===>> postamble()                                                          *
 * This routine processes the postamble. It sets the pointer to the final bop *
 * in the dvi file and checks agreement with the corresponding values from    *
 * the preamble.                                                              *
 ******************************************************************************/
postamble()
{ int c,t;
  long p;
#ifdef MACROS
  register int _a;
#endif

  lastpage = signedquad();
  if(numerator != signedquad())
    printf("Warning: pre-post disagreement in numerator\n");
  if(denominator != signedquad())
    printf("Warning: pre-post disagreement in denominator\n");
  if(magnification != signedquad())
    printf("Warning: pre-post disagreement in magnification\n");
  maxv = signedquad();
  maxh = signedquad();
  maxs = signedpair();
  t = signedpair();
  while(TRUE)
  { while((c = getbyte()) == nop);
    if((c >= fnt_def1) && (c <= fnt_def2))
    { p = firstpar(c);
      checkfont(p);
    }
    if(c == postpost) break;
  }
}
     


/******************************************************************************
 * ===>> preamble()                                                           *
 * This routine processes the preamble and determines the global variables    *
 * 'conv' and 'magnification. It exits dvihp if some fatal error is found  in *
 * the preamble already.                                                      *
 ******************************************************************************/
preamble()
{ int p;
  if(getbyte() != pre)
    error("Bad DVI file: first byte isn't start of preamble!\n");
  if(getbyte() != 2) printf("identification in byte 1 should be 2!");
  numerator   = signedquad();
  denominator = signedquad();
  if(numerator <=0 )
    valerror("Bad DVI file: numerator is %d !\n" , numerator);
  if(denominator <= 0 )
    valerror("Bad DVI file: denominator is %d !\n" , denominator);
  magnification = signedquad();
  convdot = (numerator / 254000.0) * (RESOLUTION / denominator)
                                   * (magnification / 1000.0) * mag_printer;
  conv = convdot ;
  p = getbyte();
  printf("'");
  while(p--) printf("%c",getbyte());
  printf("\n");
}



/**********************************************
 * ===>> printbop()                           *
 * Simply outputs the page number in progress *
 **********************************************/
printbop()
{ int k;
  printf("- Page ");
  for(k = 0 ; k <= startvals ; k++)
  { printf("%d",count[k]);
    if(k < startvals) printf(".");
    else              printf("\n");
  }
}



/*******************************************
 * ===>>  printfont()                      *
 * Simply outputs the name of a given font *
 *******************************************/
printfont()
{ char *p;
  if(!font->name) printf("UNDEFINED!\n");
  else
  { p = names + font->name;
    if(*p) printf("%s:",p);
    while(*p++);
    printf("%s\n",p);
  }
}



/*****************************************************************************
 * ===>> rotate_char()                                                       *
 * Rotate the pixel pattern, stored in pxlbuffer by 90 degrees for land-     *
 * scape printout mode. The rotated pixel pattern starts at the end of the   *
 * original pixel pattern in pxlbuffer. The pointer to the begin of the      *
 * rotated pattern is returned by rotate_char()                              *
 *****************************************************************************/
byte *rotate_char()
{ int lbit, tbit;
  int width, height, bytes, length;
  int mask;
  byte *pp, *lp, *tp;
  register int i,j;

  width  = c_height;
  height = c_width;
  bytes = (height + 7) / 8;
  length = height*((width + 7)/8);
  if(endofchardata - pxlbuffer + length >= MAXPXLSIZE)
       error("Out of memory while rotating character!\n");
  lp = endofchardata;
  for(i = 0 ; i < length ; i++) *lp++ = 0;
  lp = endofchardata - 1;
  tp = pxlbuffer - 1;
  tbit = 7 - (height -1) % 8;
  for(i = 0 ; i < height; i++)
  { if(tbit == 8)   { tbit = 0; tp--; }
    pp = tp + bytes;
    mask = power[tbit++];
    lp++;
    lbit = 8;
    for(j = 0 ; j < width ; j++)
    { if(!lbit--)   { lbit = 7; lp++; }
      if(*pp & mask) *lp |= power[lbit];
      pp += bytes;
    }
  }
  return(endofchardata);
}



/******************************************************************************
 * ===>>  scanbop()                                                           *
 * This routine simply scan the 10 counts after the occurence of a bop command*
 * In addition it determines the 'lastpage' and 'prevpage' pointers.          *
 ******************************************************************************/
scanbop()
{ int k;
  lastpage = ftell(dvifile);
  for(k = 0; k <= 9 ; k++) count[k] = signedquad();
  prevpage = signedquad();
}



/******************************************************************************
 * ===>>  scan_page()                                                         *
 * This routine will scan a page from 'bop' to 'eop', maintaining information *
 * about font usage. It also catches errors like illegal font numbers or cha- *
 * racters. Finally it also counts the number of pages actually processed, so *
 * if the file is scanned backwards, it will end correctly.                   *
 ******************************************************************************/
scan_page()
{ register int k;
  int  q;
  long p;
  short badchar;
#ifndef IIP
  int font_used[MAXFONTS];
  int font_num;
  for(k = 0 ; k < MAXFONTS ; k++) font_used[k] = FALSE;
  font_num = 0;
#endif
  actualpagecount++;
  while (TRUE)
  { if((k = getbyte()) < set1) (cbase + k)->use_count++;
    else
    { p = firstpar(k);
      switch (k)
      { case bop:
	  scanbop();
          break;
        case eop:
          goto end;
          break;
        case set1:
        case set2:
        case set3:
        case set4:
        case put1:
        case put2:
        case put3:
        case put4:
          if(p > 255) error("character greater 255 encountered!\n");
          else (cbase +  p)->use_count++;
          break;
        case set_rule:
        case put_rule:
          signedquad();
          break;
        case fnt_def1:
        case fnt_def2:
        case fnt_def3:
        case fnt_def4:
          definefont(p);
          break;
        case xxx1:
        case xxx2:
        case xxx3:
        case xxx4:
          badchar = FALSE;
          if(p + nextnamesfree  - names > NAMESIZE)
            error("Out of string space during special!\n");
          for (k=0; k<p; k++)
          { q = getbyte();
            if((q < ' ') || (q > '~')) badchar = TRUE;
            *(nextnamesfree + k) = q;
          }
          *(nextnamesfree + p) = '\0';
          if(badchar) printf("non-ASCII character in xxx command\n");
          dospecial();
          break;
        case post:
          inpostamble = TRUE;
          goto end;
          break;
        default:
          if((k >= fnt_num_0) && (k <= fnt4))
          { if(p > MAXFONTS)
	      error("dvihp cannot handle font number > MAXFONTS!\n");
            else
            { font = fontptr[p];
              if(font == NULL) error("Undefined font!\n");
#ifndef IIP
	      else
	      { cbase = font->chr;
		if(!font_used[p])
		{ font_used[p] = TRUE;
		  font_num++;
		}
	      }
#else
	      else cbase = font->chr;
#endif        
            }
          }
          break;
      }
    }
  }
#ifndef IIP
  end: if(font_num > 16)
       { printf("Warning: ==>  %d [>16] different fonts on ",font_num);
         printbop();
       }
#else
  end:;
#endif
}


	
/******************************************************************************
 * ===>>  skip_font_def                                                       *
 * We need a routine to skip over a font definition. This is very simple, just*
 * requires skipping over the parameters.                                     *
 ******************************************************************************/
skip_font_def()
{ int skip;
  signedquad();
  signedquad();
  signedquad();
  skip = getbyte() + getbyte();
  fseek(dvifile,skip,1);
}



#ifndef IID

/******************************************************************************
 * ===>>  skip_next_page()                                                    *
 * This routine is used to pass over the next odd page during the first path  *
 * for the 'twosided' option in which the even pages are produced.            *
 ******************************************************************************/
skip_next_page()
{ int k;
  long p;
  while (TRUE)
  { if((k = getbyte()) >= set1)
    { p = firstpar(k);
      switch(k)
      { case bop:
          scanbop();
	  break;
	case eop:
	  return(TRUE);
	  break;
        case set_rule:
        case put_rule:
          signedquad();
          break;
        case fnt_def1:
        case fnt_def2:
        case fnt_def3:
        case fnt_def4:
          skip_font_def();
          break;
        case xxx1:
        case xxx2:
        case xxx3:
        case xxx4:
          while (p-- > 0) getbyte();
          break;
        case post:
          inpostamble = TRUE;
          return(FALSE);
	  break;
        default:
          break;
      }
    }
  }
}
#endif



/******************************************************************************
 * ===>>  skip_pages()                                                        *
 * This routine is used to pass over pages that are not being translated. It  *
 * is assumed to begin just after the preamble has been read, or just after a *
 * bop has been processed. It continues until either finding a bop that mat-  *
 * ches the desired starting page specification, or until running into the    *
 * postamble                                                                  *
 ******************************************************************************/
skip_pages()
{ int i,k,q;
  long p;
  int badchar,match;
  while (TRUE)
  { if((k = getbyte()) >= set1)
    { p = firstpar(k);
      switch(k)
      { case bop:
	  scanbop();
	  match = TRUE;
	  for(i = 0 ; i <= startvals ; i++)
	    if(startthere[i] && (startcount[i] != count[i])) match = FALSE;
	  if(match)
	  { startloc = lastpage - 1;
            goto end;
          }
          break;
        case set_rule:
        case put_rule:
          signedquad();
          break;
        case fnt_def1:
        case fnt_def2:
        case fnt_def3:
        case fnt_def4:
          definefont(p);
          break;
        case xxx1:
        case xxx2:
        case xxx3:
        case xxx4:
          badchar = FALSE;
          if(p + nextnamesfree - names > NAMESIZE)
            error("Out of string space during special!\n");
          for (k=0; k<p; k++)
          { q = getbyte();
            if((q < ' ') || (q > '~')) badchar = TRUE;
            *(nextnamesfree + k) = q;
          }
          *(nextnamesfree + p) = 0;
          if(badchar) printf("non ASCII character in xxx command\n");
          dospecial();
          break;
        case post:
          inpostamble = TRUE;
          goto end;
          break;
        default:
          break;
      }
    }
  }
  end:;
}



/****************************************************************************
 * ===> sort_fonts(list)                                                    *
 * sorts order of fonts used in the dvi-file and not stored permanently in  *
 * the printers memory in respect to font usage. Therefore a rating value   *
 * is calculated for each font from their character usage in the document.  *
 * The fonts are ordered in `fontlist' in order of their rating value       *
 * `usage'. The function returns the number of different fonts used, but    *
 * not stored  permanently. As a side effect, for each font the first and   *
 * last character, used in the document ist stored in font->bc and font->ec *
 * resp., to avoid unnecessary searching later on.                          *
 ****************************************************************************/
sort_fonts(fontlist)
int fontlist[];
{ int fontnum = 0;
  long usage[MAXFONTS];
  charfmt *u, *cbase;
  long different, total;
  register int f,i;
  int store_all;
  int t;
  long tt;

  /* determine rating value for each font used but not loaded permanently */
  for (f = 0 ; f < MAXFONTS ; f++)
    if ((font = fontptr[f]) && (font->down < 0)) 
    { cbase = font->chr;
      total = different = 0;
      font->bc = 256;
      font->ec = -1;
      for(i = 0 ; i < 256 ; i++)
	if((cbase + i)->use_count)        
	{ font->bc = i ; break ; }                      /* first char used */
      for(i = 255 ; i >= font->bc ; i--)
	if((cbase + i)->use_count)
	{ font->ec = i ; break ; }                      /* last char used  */
      for(i = font->bc ; i <= font->ec ; i++)
      { u = cbase + i;
	if(u->use_count)
	{ different++ ; total += u->use_count; }   /* different used chars */
      }                                            /* and total usage      */ 
      fontlist[fontnum] = f;
      if(different)
        usage[fontnum++]  = (256*(total - different))/different; /* rating */
      else usage[fontnum++] = 0;
    }

  /* now sort used fonts in order of the usage rating values               */
  for(f = fontnum - 1; f > 0 ; f--)
    for(i = 0 ; i < f ; i++)
      if(usage[i] < usage[f])
      { tt = usage[i] ;       usage[i] = usage[f] ;       usage[f] = tt;
	t  = fontlist[i] ; fontlist[i] = fontlist[f] ; fontlist[f] = t;
      }
  return(fontnum);     /* number of different fonts not permanently loaded */
}



/******************************************************************************
 * ===>>  storefont(f)                                                        *
 * Allocate memory for gfont structure and store font in memory               *
 ******************************************************************************/
storefont(f)
int f;
{ register int i;
  charfmt *u;
  if(outmode) printf("Font will be stored in memory\n");
  gfont = (gfontfmt) malloc(sizeof(*gfont));
  gfontptr[f] = gfont;
  for(i = font->bc ; i <= font->ec ; i++)
  { u = cbase + i;
    if(u->use_count)
    { if(u->use_count > 0) u->use_count = -u->use_count;
      store_character(i);
    }
  }
}




/****************************************************************************
 * ===>> store_character()                                                  *
 * Does the corresponding things as down_character, but instead of putting  *
 * the character's pixel pattern onto bitfile it is stored in dynamicaly    *
 * allocated memory, used for drawing the character in graphic mode. In     *
 * graphic mode the meaning for x_offset and y_offset is different from     *
 * downloaded characters and the pixel pattern is stripped off from pending *
 * zero bytes in each row.                                                  *
 ****************************************************************************/
store_character(c)
int c;
{ int width, height, x_offset, y_offset;
  long length;
  byte *q, *p;
  gcharfmt *g;
  register int i;

  pktopxl(c);
  g = &(*gfont)[c];
  if(landscape)
  { p = rotate_char();
    g->pxl_bytes = width  = (c_height + 7)/8;
    g->pxl_rows  = height = c_width;
    g->x_offset  = height - c_hoffset - 1;
    g->y_offset  = -c_voffset;
  }
  else
  { p = (byte *) pxlbuffer;
    g->pxl_bytes = width  = (c_width + 7)/8;
    g->pxl_rows  = height = c_height;
    g->x_offset = -c_hoffset;
    g->y_offset = -c_voffset;
  }
 
  length = height*width;
  g->pxl_pattern = (byte *) malloc(length);
  q = g->pxl_pattern;
  for(i = 0 ; i < length ; i ++) *q++ = *p++;

}
  


   
