summaryrefslogtreecommitdiff
path: root/dviware/dviimp
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /dviware/dviimp
Initial commit
Diffstat (limited to 'dviware/dviimp')
-rw-r--r--dviware/dviimp/diext.c364
-rw-r--r--dviware/dviimp/diext.h41
-rw-r--r--dviware/dviimp/dviimp.ch1059
-rw-r--r--dviware/dviimp/dviimp.web4261
-rw-r--r--dviware/dviimp/makefile28
-rw-r--r--dviware/dviimp/specials.tex32
-rw-r--r--dviware/dviimp/warning15
7 files changed, 5800 insertions, 0 deletions
diff --git a/dviware/dviimp/diext.c b/dviware/dviimp/diext.c
new file mode 100644
index 0000000000..042c46975f
--- /dev/null
+++ b/dviware/dviimp/diext.c
@@ -0,0 +1,364 @@
+/*
+ External procedures for use with dviimp.
+ Written by H. Trickey, 6/25/85.
+
+ Implement the following procedures (as declared in Pascal)
+
+ function gfbyte: integer;
+ function gftwobytes: integer;
+ function gfthreebytes: integer;
+ function gfsignedquad: integer;
+ function getbyte: integer;
+ function signedbyte: integer;
+ function gettwobytes: integer;
+ function signedpair: integer;
+ function getthreebytes: integer;
+ function signedtrio: integer;
+ function signedquad: integer;
+ function curpos(var f:bytefile):integer;
+ procedure imbyte(w:integer);
+ procedure imhalfword(w:integer);
+ procedure setpos(var f:bytefile;n:integer);
+ procedure setpaths;
+ function testaccess(accessmode:integer; filepath:integer):boolean;
+ procedure setusername;
+*/
+
+#include <stdio.h>
+#include <strings.h>
+#include <pwd.h>
+#include "h00vars.h" /* pc runtime structures */
+#include "texpaths.h"
+
+extern struct iorec dvifile,gffile,imfile; /* pc file structs */
+extern int eofdvifile,eofgffile; /* need to be set true when these happens */
+extern int curloc; /* needs to be incremented when a dvi byte is read */
+extern int curgfloc; /* needs to be incremented when a gf byte is read */
+extern int imbyteno; /* needs to be incremented when an im byte is written */
+
+/* return unsigned version of next byte in gffile */
+int gfbyte()
+{
+ register int c;
+
+ if ((c = getc(gffile.fbuf))==EOF) {
+ eofgffile = -1;
+ return(0);
+ }
+ curgfloc++;
+ return(c);
+}
+
+/* return unsigned version of next two bytes in gffile */
+int gftwobytes()
+{
+ register int a,b;
+ register FILE *iop = gffile.fbuf;
+ a = getc(iop);
+ eofgffile = ((b = getc(iop))==EOF) ? -1 : 0;
+ curgfloc += 2;
+ return((a << 8) | b);
+}
+
+/* return unsigned version of next three bytes in gffile */
+int gfthreebytes()
+{
+ register int a,b,c;
+ register FILE *iop = gffile.fbuf;
+ a = getc(iop);
+ b = getc(iop);
+ eofgffile = ((c = getc(iop))==EOF) ? -1 : 0;
+ curgfloc +=3;
+ return((a << 16) | (b << 8) | c);
+}
+
+/* return signed version of next four bytes in gffile */
+int gfsignedquad()
+{
+ register int a,b,c,d;
+ register FILE *iop = gffile.fbuf;
+ a = getc(iop);
+ b = getc(iop);
+ c = getc(iop);
+ eofgffile = ((d = getc(iop))==EOF) ? -1 : 0;
+ curgfloc += 4;
+ return((a << 24) | (b << 16) | (c << 8) | d);
+}
+
+/* return unsigned version of next byte in dvifile */
+int getbyte()
+{
+ register int c;
+
+ if ((c = getc(dvifile.fbuf))==EOF) {
+ eofdvifile = -1;
+ return(0);
+ }
+ curloc++;
+ return(c);
+}
+
+/* return signed version of next byte in dvifile */
+int signedbyte()
+{ register int c;
+ if ((c = getc(dvifile.fbuf))==EOF) {
+ eofdvifile = -1;
+ return(0);
+ }
+ curloc++;
+ if (c>127) return(c-256);
+ return(c);
+}
+
+/* return unsigned value of next two bytes (high order first) */
+int gettwobytes()
+{
+ register int a,b;
+ register FILE *iop = dvifile.fbuf;
+ a = getc(iop);
+ eofdvifile = ((b = getc(iop))==EOF) ? -1 : 0;
+ curloc += 2;
+ return((a << 8) | b);
+}
+
+/* return signed value of next two bytes (high order first) */
+int signedpair()
+{
+ register int a,b;
+ register FILE *iop = dvifile.fbuf;
+ if ( (a = getc(iop)) > 127) a -= 256; /* sign extend */
+ eofdvifile = ((b = getc(iop))==EOF) ? -1 : 0;
+ curloc += 2;
+ return((a << 8) | b);
+}
+
+/* return unsigned value of next three bytes */
+int getthreebytes()
+{
+ register int a,b,c;
+ register FILE *iop = dvifile.fbuf;
+ a = getc(iop);
+ b = getc(iop);
+ eofdvifile = ((c = getc(iop))==EOF) ? -1 : 0;
+ curloc +=3;
+ return((a << 16) | (b << 8) | c);
+}
+
+/* return signed value of next three bytes */
+int signedtrio()
+{
+ register int a,b,c;
+ register FILE *iop = dvifile.fbuf;
+ if ( (a = getc(iop)) > 127) a -= 256;
+ b = getc(iop);
+ eofdvifile = ((c = getc(iop))==EOF) ? -1 : 0;
+ curloc +=3;
+ return((a << 16) | (b << 8) | c);
+}
+
+/* return value of next four bytes */
+int signedquad()
+{
+ register int a,b,c,d;
+ register FILE *iop = dvifile.fbuf;
+ a = getc(iop);
+ b = getc(iop);
+ c = getc(iop);
+ eofdvifile = ((d = getc(iop))==EOF) ? -1 : 0;
+ curloc += 4;
+ return((a << 24) | (b << 16) | (c << 8) | d);
+}
+
+/* put the low order byte of w on imfile */
+imbyte(w)
+ int w;
+{
+ putc(w, imfile.fbuf);
+ imbyteno++;
+}
+
+/* put the two low order bytes of w on imfile, high order first */
+imhalfword(w)
+ int w;
+{
+ putc(w >> 8, imfile.fbuf);
+ putc(w, imfile.fbuf);
+ imbyteno += 2;
+}
+
+/* seek to byte n of file f; maintain eof's if f is dvifile or gffile */
+setpos(f,n)
+ register struct iorec *f;
+ int n;
+{
+ if (n>=0) {
+ fseek(f->fbuf,(long) n,0);
+ if (f==&dvifile) eofdvifile = 0;
+ else if (f==&gffile) eofgffile =0;
+ }
+ else {
+ fseek(f->fbuf, (long) n, 2);
+ if (f==&dvifile) eofdvifile = -1;
+ else if (f==&gffile) eofgffile = -1;
+ }
+}
+
+/* return current byte offset in file f */
+int curpos(f)
+ struct iorec *f;
+{
+ return((int) ftell(f->fbuf));
+}
+
+char *fontpath;
+
+char *getenv();
+
+/*
+ * setpaths is called to set up the pointer fontpath
+ * as follows: if the user's environment has a value for TEXFONTS
+ * then use it; otherwise, use defaultfontpath.
+ */
+setpaths()
+{
+ register char *envpath;
+
+ if ((envpath = getenv("TEXFONTS")) != NULL)
+ fontpath = envpath;
+ else
+ fontpath = defaultfontpath;
+}
+
+#define namelength 100 /* should agree with dvitype.ch */
+extern char curname[],realnameoffile[]; /* these have size namelength */
+
+/*
+ * testaccess(amode,filepath)
+ *
+ * Test whether or not the file whose name is in the global curname
+ * can be opened for reading (if mode=READACCESS)
+ * or writing (if mode=WRITEACCESS).
+ *
+ * The filepath argument is one of the ...FILEPATH constants defined below.
+ * If the filename given in curname does not begin with '/', we try
+ * prepending all the ':'-separated areanames in the appropriate path to the
+ * filename until access can be made, if it ever can.
+ *
+ * The realnameoffile global array will contain the name that yielded an
+ * access success.
+ */
+
+#define READACCESS 4
+#define WRITEACCESS 2
+
+#define NOFILEPATH 0
+#define FONTFILEPATH 3
+
+bool
+testaccess(amode,filepath)
+ int amode,filepath;
+{
+ register bool ok;
+ register char *p;
+ char *curpathplace;
+ int f;
+
+ switch(filepath) {
+ case NOFILEPATH: curpathplace = NULL; break;
+ case FONTFILEPATH: curpathplace = fontpath; break;
+ }
+ if (curname[0]=='/') /* file name has absolute path */
+ curpathplace = NULL;
+ do {
+ packrealnameoffile(&curpathplace);
+ if (amode==READACCESS)
+ /* use system call "access" to see if we could read it */
+ if (access(realnameoffile,READACCESS)==0) ok = TRUE;
+ else ok = FALSE;
+ else {
+ /* WRITEACCESS: use creat to see if we could create it, but close
+ the file again if we're OK, to let pc open it for real */
+ f = creat(realnameoffile,0666);
+ if (f>=0) ok = TRUE;
+ else ok = FALSE;
+ if (ok)
+ close(f);
+ }
+ } while (!ok && curpathplace != NULL);
+ if (ok) { /* pad realnameoffile with blanks, as Pascal wants */
+ for (p = realnameoffile; *p != '\0'; p++)
+ /* nothing: find end of string */ ;
+ while (p < &(realnameoffile[namelength]))
+ *p++ = ' ';
+ }
+ return (ok ? -1 : 0);
+}
+
+/*
+ * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
+ * followed by '/', followed by the characters in curname up until the
+ * first blank there, and finally a '\0'. The cpp pointer is left pointing
+ * at the next directory in the path.
+ * But: if *cpp == NULL, then we are supposed to use curname as is.
+ */
+packrealnameoffile(cpp)
+ char **cpp;
+{
+ register char *p,*realname;
+
+ realname = realnameoffile;
+ if ((p = *cpp)!=NULL) {
+ while ((*p != ':') && (*p != '\0')) {
+ *realname++ = *p++;
+ if (realname == &(realnameoffile[namelength-1]))
+ break;
+ }
+ if (*p == '\0') *cpp = NULL; /* at end of path now */
+ else *cpp = p+1; /* else get past ':' */
+ *realname++ = '/'; /* separate the area from the name to follow */
+ }
+ /* now append curname to realname... */
+ p = curname;
+ while (*p != ' ') {
+ if (realname >= &(realnameoffile[namelength-1])) {
+ fprintf(stderr,"! Full file name is too long\n");
+ break;
+ }
+ *realname++ = *p++;
+ }
+ *realname = '\0';
+}
+
+extern struct passwd * getpwuid();
+extern char username[];
+extern int lenusername;
+/*
+ * find out the caller's name, putting it into username[1..namelength]
+ * and put the length used into lenusername
+ */
+setusername()
+{
+ int i;
+ char *nam;
+
+ nam = getpwuid(getuid())->pw_name;
+ for (i=0; *nam!=0 && *nam!='(' && i<namelength; )
+ username[i++] = *nam++;
+ lenusername = i;
+}
+
+extern char hostname[];
+extern int lenhostname;
+sethostnam()
+{
+ int i;
+ char nam[namelength];
+
+ lenhostname = 0;
+ if (gethostname(nam,255) != 0) return;
+ for (i=0; nam[i]!=0 && nam[i]!='.' && i<namelength; ) {
+ hostname[i] = nam[i];
+ i++;
+ }
+ lenhostname = i;
+}
diff --git a/dviware/dviimp/diext.h b/dviware/dviimp/diext.h
new file mode 100644
index 0000000000..ea831f18c4
--- /dev/null
+++ b/dviware/dviimp/diext.h
@@ -0,0 +1,41 @@
+function gfbyte: integer;
+ external;
+function gftwobytes: integer;
+ external;
+function gfthreebytes: integer;
+ external;
+function gfsignedquad: integer;
+ external;
+function getbyte: integer;
+ external;
+function signedbyte: integer;
+ external;
+function gettwobytes: integer;
+ external;
+function signedpair: integer;
+ external;
+function getthreebytes: integer;
+ external;
+function signedtrio: integer;
+ external;
+function signedquad: integer;
+ external;
+procedure imbyte(w:integer);
+ external;
+procedure imhalfword(w:integer);
+ external;
+function curpos(var f:bytefile):integer;
+ external;
+procedure setpos(var f:bytefile;n:integer);
+ external;
+procedure setpaths;
+ external;
+function testaccess(accessmode:integer; filepath:integer): boolean;
+ external;
+procedure setusername;
+ external;
+procedure sethostnam;
+ external;
+procedure exit(i:integer);
+ external;
+
diff --git a/dviware/dviimp/dviimp.ch b/dviware/dviimp/dviimp.ch
new file mode 100644
index 0000000000..9d6ede8d02
--- /dev/null
+++ b/dviware/dviimp/dviimp.ch
@@ -0,0 +1,1059 @@
+% Change file for the DVIIMP processor, for use on Berkeley UNIX systems.
+% It uses the pc compiler. The resulting Pascal program must be run through
+% "pxp -O" to handle the "others" clauses in case statements.
+% This file was created by Howard Trickey.
+
+% History:
+% 6/25/85 (HWT) Created, based on dvip.ch
+% 7/10/85 (HWT) Version 0.92
+% 12/5/87 (PAM) to Version 0.94
+@x
+% Here is TeX material that gets inserted after \input webmac
+@y
+\let\maybe=\iffalse
+@z
+@x
+ \centerline{(Version 0.94, November 1987)}
+@y
+ \centerline{(Version 0.94, November 1987, for Berkeley UNIX)}
+@z
+
+
+@x banner
+@d banner=='This is DVIIMP, Version 0.94' {printed when the program starts}
+@y
+@d banner=='This is DVIIMP, Version 0.94 for Berkeley UNIX'
+ {printed when the program starts}
+@z
+
+@x debug..gubed
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@y
+@d debug==@{
+@d gubed==@t@>@}
+@z
+
+@x declare input file
+@p program DVI_IMP(@!dvi_file,@!im_file,@!output);
+@y
+@p program DVI_IMP(@!dvi_file,@!im_file,@!input,@!output);
+@z
+
+@x Add inclusion of diext.h
+procedure initialize; {this procedure gets things started properly}
+@y
+@#
+@\@=#include "diext.h"@>@\ {declarations for external procedures}
+procedure initialize; {this procedure gets things started properly}
+@z
+
+@x longer file names
+@!name_length=50; {a file name shouldn't be longer than this}
+@y
+@!name_length=100; {a file name shouldn't be longer than this}
+@z
+
+@x Change jump_out to an exit(1) (so we can test exit status)
+@p procedure jump_out;
+begin goto final_end;
+end;
+@y
+@d UNIX_exit==e@&x@&i@&t {Tangle trick}
+
+@p procedure jump_out;
+begin print_ln(' '); UNIX_exit(1);
+end;
+@z
+
+@x Change definition of 'byte_file' to correct subrange
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+On Berkeley {\mc UNIX}, we have to use |-128..127| for byte files, as
+explained in the \TeX\ changes.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@z
+
+@x get rid of read_f, read_n, and read_c
+@ The following procedures will be needed.
+
+@p function read_int:integer;
+var i:integer;
+@!neg_flag:boolean;
+begin
+neg_flag:=false; i:=0;
+get(tty);
+while tty^=' ' do get(tty);
+if (tty^='-') then neg_flag:=true;
+while (tty^='-') or (tty^='+') do get(tty);
+while (tty^>='0') and (tty^<='9') do begin
+ i:=i*10+xord[tty^]-"0"; get(tty);
+ end;
+if neg_flag then i:=-i;
+read_int:=i;
+end;
+@#
+procedure read_f;
+begin
+start_page:=read_int;
+f_flag:=true;
+end;
+@#
+procedure read_n;
+begin
+num_pages:=read_int;
+if num_pages=0 then num_pages:=max_pages;
+n_flag:=true;
+end;
+@#
+procedure read_c;
+begin
+copies:=read_int;
+end;
+@#
+procedure read_h;
+begin
+h_org:=read_int;
+end;
+@#
+procedure read_v;
+begin
+v_org:=read_int;
+end;
+@y
+@ The command line parsing is done elsewhere in the Berkeley UNIX version,
+since it takes many modules, so we use this module to declare some
+procedures.
+@p
+@<|scan_args|> procedure@>@/
+@<|output_im_preamble| procedure@>
+@z
+
+@x Fix up opening the binary files
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin reset(dvi_file);
+cur_loc:=0;
+end;
+@#
+procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file,cur_name);
+cur_gf_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin reset(tfm_file,cur_tfm_name);
+end;
+@y
+We use the external |test_access| procedure to find
+out if a file whose name is |cur_name|
+is accessible before attempting to open it.
+It also does path searching, based on the user's environment or the
+default path.
+The |open_tfm_file| procedure is changed into a function that returns
+false if the open fails.
+The |open_dvi_file| and |open_gf_file| procedures need to set up the
+global |eof_dvi_file| and |eof_gf_file| variables (used for efficiency).
+The latter follows the convention that |eof_gf_file| is set if the
+open fails. If the \.{DVI} file can't be opened, we just quit here.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {no path searching should be done}
+@d font_file_path=3 {path specifier for \.{TFM} and \.{GF} files}
+
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+var i:integer;
+begin
+ for i:=1 to name_length do
+ cur_name[i]:=dvi_file_name_chars[i];
+ if test_access(read_access_mode,no_file_path) then begin
+ reset(dvi_file,real_name_of_file);
+ eof_dvi_file:=false;
+ end
+ else begin
+ write_ln('Cannot read dvi file');
+ UNIX_exit(1);
+ end;
+cur_loc:=0;
+end;
+@#
+procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin
+ if test_access(read_access_mode,font_file_path) then begin
+ reset(gf_file,real_name_of_file);
+ eof_gf_file:=false;
+ end
+ else
+ eof_gf_file:=true;
+ cur_gf_loc:=0;
+end;
+@#
+function open_tfm_file:boolean; {prepares to read packed bytes in |tfm_file|}
+var i:integer;
+begin
+ for i:=1 to name_length do
+ cur_name[i]:=cur_tfm_name[i];
+ if test_access(read_access_mode,font_file_path) then begin
+ reset(tfm_file,real_name_of_file);
+ open_tfm_file:=true;
+ end
+ else
+ open_tfm_file:=false;
+end;
+@z
+
+@x opening im_file
+@p procedure open_im_file; {prepares to write packed bytes in |im_file|}
+begin rewrite(im_file); im_byte_no:=0;
+end;
+@y
+@p procedure open_im_file; {prepares to write packed bytes in |im_file|}
+var i:integer;
+begin
+ for i:=1 to name_length do
+ cur_name[i]:=im_file_name_chars[i];
+ if test_access(write_access_mode,no_file_path) then
+ rewrite(im_file, real_name_of_file)
+ else begin
+ write_ln('Cannot create impress file');
+ UNIX_exit(1);
+ end;
+ im_byte_no:=0;
+end;
+@z
+
+@x Declare real_name_of_file.
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+@y
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+@!real_name_of_file:packed array[1..name_length] of char; {filled by
+ the external |test_access| procedure: it is |cur_name|
+ with a path prepended}
+@z
+
+@x Fix gf getting functions
+@p function gf_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then gf_byte:=0
+else begin read(gf_file,b); incr(cur_gf_loc); gf_byte:=b;
+ end;
+end;
+@#
+function gf_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(gf_file,a); read(gf_file,b);
+cur_gf_loc:=cur_gf_loc+2;
+gf_two_bytes:=a*256+b;
+end;
+@#
+function gf_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c);
+cur_gf_loc:=cur_gf_loc+3;
+gf_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function gf_signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+cur_gf_loc:=cur_gf_loc+4;
+if a<128 then gf_signed_quad:=((a*256+b)*256+c)*256+d
+else gf_signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@y
+For UNIX, use external |@!gf_byte|, etc., procedures.
+Use this module to declare the |eof_gf_file| variable that will be set
+by those procedures.
+
+@<Globals...@>=
+@!eof_gf_file:boolean; {|true| when \.{.GF} file has ended}
+@z
+
+@x Fix read_tfm_word to read bytes properly
+@p procedure read_tfm_word;
+begin read(tfm_file,b0); read(tfm_file,b1);
+read(tfm_file,b2); read(tfm_file,b3);
+end;
+@y
+@d get_tfm_byte(#) ==
+ read(tfm_file,byte); if byte < 0 then # := byte + 256 else # := byte;
+
+@p procedure read_tfm_word;
+var byte : -128..127;
+begin
+get_tfm_byte(b0);
+get_tfm_byte(b1);
+get_tfm_byte(b2);
+get_tfm_byte(b3);
+end;
+@z
+
+@x Fix up functions to read DVI bytes
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(dvi_file) then get_byte:=0
+else begin read(dvi_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:eight_bits;
+begin read(dvi_file,b); incr(cur_loc);
+if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function signed_pair:integer; {returns the next two bytes, signed}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+if a<128 then signed_pair:=a*256+b
+else signed_pair:=(a-256)*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_trio:integer; {returns the next three bytes, signed}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+if a<128 then signed_trio:=(a*256+b)*256+c
+else signed_trio:=((a-256)*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@y
+For UNIX, use external |@!get_byte|, etc., procedures.
+Use this module to declare the |eof_dvi_file| variable that will be set
+by those procedures.
+
+@<Globals...@>=
+@!eof_dvi_file:boolean; {|true| when DVI file has ended}
+@z
+
+@x Change impress file writing procedures
+@d im_byte(#)==begin write(im_file,#);
+ incr(im_byte_no); end
+
+@p procedure im_sbyte(@!w:integer);
+begin
+if w<0 then w:=w+@"100;
+im_byte(w);
+end;
+@#
+procedure im_halfword(@!w:integer);
+begin
+if w<0 then w:=w+@"10000;
+im_byte(w div @"100);
+im_byte(w mod @"100);
+end;
+@y
+External C procedures are used for writing the impress file, but
+|im_sbyte| is the same as |im_byte|.
+
+@d im_sbyte(#)==im_byte(#)
+@z
+
+@x
+@ Having found the |gf_postamble|, we must now read it and stow the
+data away as as halfwords as required later by \.{IMAGEN}.
+@y
+@ Having found the |gf_postamble|, we must now read it and stow the
+data away as as halfwords as required later by \.{IMAGEN}.
+We define |other_gf_cases| to be those \.{GF} commands not expected
+during the processing of glyphs, so that the |othercases| clause can
+be removed from several critical places below.
+The method used on UNIX to get rid of |othercases| clauses produces
+incredibly awful code when the number of default cases is large enough
+that a bit-set test membership can't be generated.
+
+@d other_gf_cases==paint2+1,boc,boc1,skip2,skip2+1,char_loc,246,247,248,249,
+ 250,251,252,253,254,255
+@z
+
+LINE 1950
+@x eof(gf_file)
+while not eof(gf_file) do m:=gf_byte; {to close out file}
+@y
+while not eof_gf_file do m:=gf_byte; {to close out file}
+@z
+
+@x remove othercases from in_gf (pxp -O causes bad code here)
+ eoc: goto done;
+ othercases
+ print_ln('! Unexpected command: ',o:1)
+@y
+ eoc: goto done;
+ other_gf_cases:
+ print_ln('! Unexpected command: ',o:1)
+@z
+
+LINE 2580
+@x Define term_in and term_out
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!term_in:text_file; {the terminal, considered as an input file}
+@!term_out:text_file; {the terminal, considered as an output file}
+@y
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@d term_in==input
+@d term_out==output
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@z
+
+@x Define update_terminal
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush(term_out) {empty the terminal output buffer}
+@z
+
+LINE 2610
+@x Remove call to reset(term_in)
+begin update_terminal; reset(term_in);
+@y
+begin update_terminal;
+@z
+
+LINE 2918
+@x
+if eof(gf_file) then
+@y
+if eof_gf_file then
+@z
+
+LINE 2927
+@x open_tfm_file is a function
+ open_tfm_file;
+ if eof(tfm_file) then
+@y
+ if not open_tfm_file then
+@z
+
+LINE 2940
+@x Set default_directory_name
+@d default_directory_name=='TeXGFs:' {change this to the correct name}
+@d default_directory_name_length=7 {change this to the correct length}
+@d dflt_tfm_directory_name=='TeXfonts:' {change this to the correct name}
+@d dflt_tfm_directory_name_length=9 {change this to the correct length}
+
+@<Glob...@>=
+@!default_directory:packed array[1..default_directory_name_length] of char;
+@!dflt_tfm_directory:packed array[1..dflt_tfm_directory_name_length] of char;
+@y
+Actually, under UNIX the standard area is defined in an external
+``texpaths.h'' file. And the user has a path serached for fonts,
+by setting the TEXFONTS environment variable.
+@z
+
+@x don't need to initialize now-deleted default directories
+@ @<Set init...@>=
+default_directory:=default_directory_name;
+dflt_tfm_directory:=dflt_tfm_directory_name;
+@y
+@ (Deleted module).
+@z
+
+LINE 2980
+@x GF files don't get directories prepended, and stay lowercase
+\.{GF} file for the current font. This usually means that we need to
+prepend the name of the default directory, and
+to append the suffix `\.{.GF}'. Furthermore, we change lower case letters
+to upper case, since |cur_name| is a \PASCAL\ string.
+@^system dependencies@>
+
+@<Move font name into the |cur_name| string@>=
+for k:=1 to name_length do cur_name[k]:=' ';
+if p=0 then
+ begin for k:=1 to default_directory_name_length do
+ cur_name[k]:=default_directory[k];
+ r:=default_directory_name_length;
+ end
+else r:=0;
+for k:=font_name[cur_font] to font_name[cur_font+1]-1 do
+ begin incr(r);
+ if r+4>name_length then
+ abort('DVIIMP capacity exceeded (max font name length=',
+ name_length:1,')!');
+@.DVIIMP capacity exceeded...@>
+ if (names[k]>="a")and(names[k]<="z") then
+ cur_name[r]:=xchr[names[k]-@'40]
+ else cur_name[r]:=xchr[names[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='G'; cur_name[r+3]:='F';
+{|cur_name[r+4]:='M';|}
+@y
+\.{GF} file for the current font. The directory name will be prepended by
+|test_access|.
+As at {\mc SAIL}, we use the overall font magnification as the extension,
+using a funny scheme where the magnification 999 is followed by :00
+(since `:' comes after 9 in the ASCII sequence).
+@^system dependencies@>
+
+@<Move font name into the |cur_name| string@>=
+for k:=1 to name_length do cur_name[k]:=' ';
+r:=0;
+for k:=font_name[cur_font] to font_name[cur_font+1]-1 do
+ begin incr(r);
+ if r+6>name_length then
+ abort('DVIIMP capacity exceeded (max font name length=',
+ name_length:1,')!');
+@.DVIIMP capacity exceeded...@>
+ cur_name[r]:=xchr[names[k]];
+ end;
+m:=font_m_val[cur_font];
+cur_name[r+1]:='.';
+cur_name[r+2]:=xchr[m div 100+@'60];
+cur_name[r+3]:=xchr[(m mod 100) div 10+@'60];
+cur_name[r+4]:=xchr[m mod 10+@'60];
+cur_name[r+5]:='g';
+cur_name[r+6]:='f';
+@z
+
+@x finding TFM files; no default directory
+The following module takes care of setting the external name of this
+\.{TFM} file.
+
+@<Move font name into the |cur_tfm_name| string@>=
+for k:=1 to name_length do cur_tfm_name[k]:=' ';
+if p=0 then
+ begin for k:=1 to dflt_tfm_directory_name_length do
+ cur_tfm_name[k]:=dflt_tfm_directory[k];
+ r:=dflt_tfm_directory_name_length;
+ end
+else r:=0;
+for k:=font_name[cur_font] to font_name[cur_font+1]-1 do
+ begin incr(r);
+ if r+4>name_length then
+ abort('DVIIMP capacity exceeded (max font name length=',
+ name_length:1,')!');
+@.DVIIMP capacity exceeded...@>
+ if (names[k]>="a")and(names[k]<="z") then
+ cur_tfm_name[r]:=xchr[names[k]-@'40]
+ else cur_tfm_name[r]:=xchr[names[k]];
+ end;
+cur_tfm_name[r+1]:='.'; cur_tfm_name[r+2]:='T';
+cur_tfm_name[r+3]:='F'; cur_tfm_name[r+4]:='M';
+
+@y
+The following module takes care of setting the external name of this
+\.{TFM} file.
+
+@<Move font name into the |cur_tfm_name| string@>=
+for k:=1 to name_length do cur_tfm_name[k]:=' ';
+r:=0;
+for k:=font_name[cur_font] to font_name[cur_font+1]-1 do
+ begin incr(r);
+ if r+4>name_length then
+ abort('DVIIMP capacity exceeded (max font name length=',
+ name_length:1,')!');
+@.DVIIMP capacity exceeded...@>
+ cur_tfm_name[r]:=xchr[names[k]];
+ end;
+cur_tfm_name[r+1]:='.'; cur_tfm_name[r+2]:='t';
+cur_tfm_name[r+3]:='f'; cur_tfm_name[r+4]:='m';
+@z
+
+@x update_terminal after printing page number
+ do_page; print('[',count[0]:1,'] ');
+@y
+ do_page; print('[',count[0]:1,'] '); update_terminal;
+@z
+
+LINE 3752
+@x
+if eof(dvi_file) then bad_dvi('the file ended prematurely');
+@y
+if eof_dvi_file then bad_dvi('the file ended prematurely');
+@z
+
+LINE 3769
+@x fix case statement in dopage so that othercases isn't needed
+if o<set_char_0+128 then goto fin_set
+else case o of
+@y
+if o<set_char_0+128 then goto fin_set
+else if o<down1 then case o of
+@z
+
+@x continue previous change
+ othercases begin special_cases(o,p,a); goto done; end
+ endcases
+@y
+ endcases
+else begin special_cases(o,p,a); goto done; end
+@z
+
+@x Remove need for othercases from special_cases (lower test done at call)
+case o of
+@t\4@>@<Cases for vertical motion@>@;
+@y
+if o<=post_post then
+ case o of
+ @t\4@>@<Cases for vertical motion@>@;
+@z
+
+@x
+othercases bad_dvi('undefined command ',o:1,'!')
+@.undefined command@>
+endcases;
+@y
+ endcases
+else
+ bad_dvi('undefined command ',o:1,'!');
+@.undefined command@>
+@z
+
+LINE 4176
+@x scan_args
+@p begin initialize; {get all variables initialized}
+@y
+@p begin initialize; {get all variables initialized}
+ scan_args;
+@z
+
+LINE 4178
+@x file and user id preamble to impress file (vars are set by set_paths_
+open_im_file;
+@y
+open_im_file;
+output_im_preamble;
+@z
+
+LINE 4182
+@x new-line before exit and exit(0)
+final_end:end.
+@y
+print_ln(' ');
+UNIX_exit(0);
+final_end:end.
+@z
+
+@x fix header-printing bug (is it realy a bug, or just unix?)
+ begin incr(p); id[p]:=get_byte;
+@y
+ begin id[p]:=get_byte; incr(p);
+@z
+
+@x Extra declarations.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{DVIIMP} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+Here is the stuff for command line processing.
+@^system dependencies@>
+
+@<Glob...@>=
+@!num_copies:integer;
+@!print_impress_header,insert_grant_name:boolean;
+@!dvi_file_name_chars,
+@!grant_name,
+@!im_file_name_chars:packed array [1..name_length] of char;
+ {string form of file names}
+
+@ The |scan_args| procedure looks at the command line arguments and sets
+the file name variables accordingly.
+At least one file name must be present: the \.{DVI} file. It may have
+an extension, or it may omit it to get |'.dvi'| added.
+The \.{IMPRESS} output file name is formed by replacing the \.{DVI} file
+name extension by |'.im'|.
+
+An argument beginning with a minus sign is a flag.
+Any letters following the minus sign may cause global flag variables to be
+set.
+A |-i| means the next argument should be used as the \.{IMPRESS} file.
+A |-f| means the next argument is the first page to print.
+A |-n| means the next argument is the number of pages to print.
+A |-c| means the next argument is the number of copies of the document to produce.
+A |-h| means do not print the header page.
+A |-g| means insert the next argument into the "grant" field of the document header.
+
+@<|scan_args|...@>=
+@<|usage_error| procedure@>@/
+@<|getargint| function@>
+@#
+procedure scan_args;
+var dot_pos,i,a: integer; {indices}
+@!fname: array[1..name_length] of char; {temporary argument holder}
+@!found_dvi,@!found_im: boolean; {|true| when that file name has been seen}
+begin
+setpaths; {read environment, to find TEXFONTS, if there}
+found_dvi:=false;
+found_im:=false;
+print_impress_header:=true;
+insert_grant_name:=false;
+num_copies:=1;
+a:=1;
+while a<argc do begin
+ argv(a,fname); {put argument number |a| into |fname|}
+ if fname[1]<>'-' then begin
+ if not found_dvi then
+ @<Get |dvi_file_name_chars| and
+ |im_file_name_chars| variables from |fname|@>
+ else usage_error;
+ end
+ else @<Handle flag argument in |fname|@>;
+ incr(a);
+ end;
+if not found_dvi then usage_error;
+end;
+
+@ Use all of |fname| for the |dvi_file_name_chars| if there is a |'.'| in it,
+otherwise add |'.dvi'|.
+The impress file name comes from adding things after the dot.
+The |argv| procedure will not put more than |max_file_name_length-5|
+characters into |fname|, and this leaves enough room in the |file_name|
+variables to add the extensions.
+
+The end of a file name is marked with a |' '|, the convention assumed by
+the |reset| and |rewrite| procedures.
+
+@<Get |dvi_file_name_chars|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=name_length-5) do begin
+ dvi_file_name_chars[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ dvi_file_name_chars[dot_pos]:='.';
+ dvi_file_name_chars[dot_pos+1]:='d';
+ dvi_file_name_chars[dot_pos+2]:='v';
+ dvi_file_name_chars[dot_pos+3]:='i';
+ dvi_file_name_chars[dot_pos+4]:=' ';
+ end
+ else dvi_file_name_chars[i] := ' '; {terminate string}
+if not found_im then begin
+ for i:=1 to dot_pos do begin
+ im_file_name_chars[i]:=dvi_file_name_chars[i];
+ end;
+ im_file_name_chars[dot_pos+1]:='i';
+ im_file_name_chars[dot_pos+2]:='m';
+ im_file_name_chars[dot_pos+3]:=' ';
+ end;
+found_dvi:=true;
+end
+
+@ @<Handle flag...@>=
+begin
+case fname[2] of
+ 'i':begin
+ incr(a);
+ if found_im or(a>=argc) then usage_error;
+ argv(a,fname); {use next argument as impress file name}
+ i:=1;
+ while (fname[i]<>' ') and (i<=name_length-7) do begin
+ im_file_name_chars[i]:=fname[i];
+ incr(i);
+ end;
+ im_file_name_chars[i]:=' ';
+ found_im:=true;
+ end;
+ 'f':begin
+ incr(a);
+ start_page:=getargint(a);
+ f_flag:=true;
+ end;
+ 'n':begin
+ incr(a);
+ num_pages:=getargint(a);
+ n_flag:=true;
+ end;
+ 'c':begin
+ incr(a);
+ num_copies:=getargint(a);
+ end;
+ 'h':begin
+ print_impress_header:=false;
+ end;
+ 'g':begin
+ incr(a);
+ if (a >= argc) then usage_error;
+ argv(a,grant_name);
+ insert_grant_name:=true;
+ end;
+ othercases
+ usage_error;
+ endcases;
+end
+
+@ This function scans argv(argno) for an integer, and returns it, but
+prints the usage error and quits if there is no integer there.
+@<|getargint| function@>=
+function getargint(argno:integer):integer;
+var argbuf:array[1..name_length] of char;@/
+ i,d,sign,n:integer;@/
+begin
+ if argno>=argc then
+ usage_error;
+ argv(argno,argbuf);
+ i:=1;
+ if (argbuf[i]='-') then sign:=-1 @+else sign:=1;
+ if (argbuf[i]='+')or(argbuf[i]='-') then i:=i+1;
+ n:=0;
+ d:=ord(argbuf[i])-ord('0');
+ while (0<=d)and(d<=9) do begin
+ n:=10*n + d;
+ i:=i+1;
+ d:=ord(argbuf[i])-ord('0');
+ end;
+ if i=1 then
+ usage_error;
+ getargint:=sign*n;
+end;
+
+@ If there is a mistake in the arguments, print a usage message and quit.
+
+@<|usage_error| procedure@>=
+procedure usage_error;
+begin
+print_ln('Usage: dviimp dvifile[.dvi] options');
+print_ln(' -i file specify Impress output file');
+print_ln(' -c N produce N copies');
+print_ln(' -h suppress Impress header sheet');
+print_ln(' -n N print N pages');
+print_ln(' -f N begin at page N');
+print_ln(' -g grant insert grant accounting field');
+UNIX_exit(1);
+end;
+
+@ The external procedure |set_user_name| fills these variables,
+used in the following |output_im_preamble| procedure.
+@<Glob...@>=
+@!user_name:packed array[1..name_length] of char;
+ {name of user who called this program}
+@!len_user_name:integer; {the amount actually used}
+@!host_name:packed array[1..name_length] of char;
+@!len_host_name:integer;
+
+@ Here is a procedure to put some identifying characters at the beginning
+of the \.{IMPRESS} file.
+@<|output_im_preamble| procedure@>=
+procedure im_pos_integer(i:integer);
+ begin
+ if i > 0 then begin
+ im_pos_integer(i div 10);
+ im_byte(i mod 10+ord('0'));
+ end;
+end;
+procedure im_integer(i:integer);
+ begin
+ if i < 0 then begin
+ im_byte("-");
+ i := -i;
+ end;
+ if i > 0
+ then im_pos_integer(i)
+ else im_byte("0");
+end;
+procedure output_im_preamble;
+var i:integer;
+begin
+im_byte("@@");
+im_byte("d");
+im_byte("o");
+im_byte("c");
+im_byte("u");
+im_byte("m");
+im_byte("e");
+im_byte("n");
+im_byte("t");
+im_byte("(");
+
+im_byte("p");
+im_byte("a");
+im_byte("g");
+im_byte("e");
+im_byte("r");
+im_byte("e");
+im_byte("v");
+im_byte("e");
+im_byte("r");
+im_byte("s");
+im_byte("a");
+im_byte("l");
+im_byte(" ");
+im_byte("o");
+im_byte("f");
+im_byte("f");
+im_byte(",");
+im_byte(" ");
+
+im_byte("p");
+im_byte("a");
+im_byte("g");
+im_byte("e");
+im_byte("c");
+im_byte("o");
+im_byte("l");
+im_byte("l");
+im_byte("a");
+im_byte("t");
+im_byte("i");
+im_byte("o");
+im_byte("n");
+im_byte(" ");
+im_byte("o");
+im_byte("f");
+im_byte("f");
+im_byte(",");
+im_byte(" ");
+
+
+
+im_byte("l");
+im_byte("a");
+im_byte("n");
+im_byte("g");
+im_byte("u");
+im_byte("a");
+im_byte("g");
+im_byte("e");
+im_byte(" ");
+im_byte("i");
+im_byte("m");
+im_byte("P");
+im_byte("r");
+im_byte("e");
+im_byte("s");
+im_byte("s");
+im_byte(",");
+im_byte(" ");
+
+if not print_impress_header then begin
+ im_byte("j");
+ im_byte("o");
+ im_byte("b");
+ im_byte("h");
+ im_byte("e");
+ im_byte("a");
+ im_byte("d");
+ im_byte("e");
+ im_byte("r");
+ im_byte(" ");
+ im_byte("o");
+ im_byte("f");
+ im_byte("f");
+ im_byte(",");
+ end;
+
+if insert_grant_name then begin
+ im_byte("G");
+ im_byte("r");
+ im_byte("a");
+ im_byte("n");
+ im_byte("t");
+ im_byte(" ");
+ im_byte("""");
+ i:=1;
+ while (grant_name[i]<>' ') do
+ begin im_byte(xord[grant_name[i]]); incr(i);
+ end;
+ im_byte("""");
+ im_byte(",");
+ end;
+
+if num_copies > 1 then begin
+ im_byte("c");
+ im_byte("o");
+ im_byte("p");
+ im_byte("i");
+ im_byte("e");
+ im_byte("s");
+ im_byte(" ");
+ im_integer(num_copies);
+ im_byte(",");
+ end;
+
+im_byte("O");
+im_byte("w");
+im_byte("n");
+im_byte("e");
+im_byte("r");
+im_byte(" ");
+im_byte("""");
+setusername;
+for i:=1 to len_user_name do im_byte(xord[user_name[i]]);
+im_byte("""");
+im_byte(",");
+
+sethostnam;
+if len_host_name > 0 then begin
+ im_byte("H");
+ im_byte("o");
+ im_byte("s");
+ im_byte("t");
+ im_byte(" ");
+ im_byte("""");
+ for i:=1 to len_host_name do im_byte(xord[host_name[i]]);
+ im_byte("""");
+ im_byte(",");
+ end;
+
+im_byte("N");
+im_byte("a");
+im_byte("m");
+im_byte("e");
+im_byte(" ");
+im_byte("""");
+i:=1;
+while (dvi_file_name_chars[i]<>' ') do
+ begin im_byte(xord[dvi_file_name_chars[i]]); incr(i);
+ end;
+im_byte("""");
+im_byte(",");
+im_byte("D");
+im_byte("V");
+im_byte("I");
+im_byte("-");
+im_byte("i");
+im_byte("d");
+im_byte(" ");
+im_byte("""");
+for p:=1 to id_len do im_byte(id[p]);
+im_byte("""");
+im_byte(")");
+end;
+@z
diff --git a/dviware/dviimp/dviimp.web b/dviware/dviimp/dviimp.web
new file mode 100644
index 0000000000..5e5c5c803e
--- /dev/null
+++ b/dviware/dviimp/dviimp.web
@@ -0,0 +1,4261 @@
+% This program by A. L. Samuel is not copyrighted and can be used freely.
+% This program depends heavily on DVItype.WEB by D. E. Knuth for much of
+% the basic material relating to the reading of DVI files and on GFtoDOVER
+% for much of the basic material relating to the reading of GF files.
+% The idea of getting the font information directly from the GF files
+% rather than from PXL and TFM files was suggested by D. E. Knuth,
+% Several people have contributed ideas as to fast methods of doing this.
+
+% Version 0.3 now accepts as many as 50 256-character fonts and it does an
+% automatic spooling job for the Imagen with the pages properly collated.
+% Version 0.4 Corrections for the new_row_69 bug and a major clean-up by
+% D.R.Fuchs with the introduction of |debug| and |gubed| instead of the
+% earlier temporary fix.
+% Version 0.5 Fix to get TFM widths for fonts with no GF file available.
+% Version 0.6 Fix to handle |empty_glyph| cases properly, and a minor
+% change to the |reconcile_scale| routine.
+% Version 0.7 Major change to |m_store| now |mm_store|, making it to
+% store from |[0,4] through |[0,85999]| then to |[1,4]| through |[1,85999]|.
+% Version 0.8 Added switches /f, /n, and /c, being respectively, the number
+% count[0] of the first page to be printed, the total number of pages and the
+% number of copies desired.
+% Version 0.9 Added xxx{point <number>} and xxx{join <pen size> number1>
+% <number2>... special commands to locate points and draw lines.
+% Also improved /f and /n to allow for Roman and Arabic page number mixes.
+% Version 0.91 Added Imagen's version of circ_arc and ellipse_arc and made
+% several very minor bug fixes.
+% Version 0.92 Added on-line disagreement reports for check_sum, design_size,
+% and at-size. Also deleted a number of unneeded variables and cleaned things
+% up a bit.
+% Version 0.93 Fixed some off-by-one bugs in indexing the |mm_store| array.
+% (JJW)
+% Version 0.94 Fixed tfm loading ala TeX 2.7. (TGR)
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like PASCAL
+\def\PASCAL{{\mc PASCAL}}
+\let\swap=\leftrightarrow
+\font\logo=logo10 % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+
+\def\title{DVIIMP}
+\def\contentspagenumber{1}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont DVIIMP} processor}
+ \vskip 15pt
+ \centerline{(Version 0.94, November 1987)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926 and MCS-8300984,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+
+This \.{DVIIMP} program reads binary device-independent (``\.{DVI}'')
+files that are produced by document compilers such as \TeX, and converts
+them into a form acceptable to the \.{IMAGEN} printer. The primary use of
+this program will be to print documents that use a large variety of
+different fonts that are freshly prepared by the \MF\ program and with
+this use in mind the program gets the needed font information directly
+from \.{GF} files. This direct use of \.{GF} font information may set a
+trend but it should be noted that many older but still useful fonts may
+not be available in \.{GF} form. \.{DVIIMP} has been written in the
+\.{WEB} language to conform with the general practice for other programs
+of this general type and to simplify the task of adapting it for use on a
+variety of different computers and different operating systems.
+
+This program reads the \.{GF} files and stores the font information
+(somewhat compressed and simplified from the \.{GF} file format) in an
+array called |mm_store|, and only translates the detailed raster
+information into the needed \.{imPRESS} format a glyph at a time on the first
+occurence of each needed glyph in the document being translated. This
+requires a rather involved procedure for keeping a record of those glyphs
+that have already been transmitted and of providing for the possibilities
+that the memory space allowed for fonts in the main memory associated with
+this program and the internal memory within the \.{IMAGEN} for glyphs may
+not be large enough for the job without arranging for the deletion of some
+font information and its possible replacement should it again prove to be
+needed.
+
+There seems to be a 2-to-the-17th-pixel limit to the maximum permitted
+size of glyph that IMAGEN will accept, measured as the product of the
+glyph's width (rounded up to a whole number of bytes) and its height.
+
+The |banner| string defined here should be changed whenever \.{DVIIMP}
+gets modified.
+
+@d banner=='This is DVIIMP, Version 0.94' {printed when the program starts}
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@f debug==begin
+@f gubed==end
+
+@ This program is written in standard \PASCAL, except where it is necessary
+to use extensions; for example, \.{DVIIMP} must read files whose names
+are dynamically specified, and that would be impossible in pure \PASCAL.
+All places where nonstandard constructions are used have been listed in
+the index under ``system dependencies.''
+@!@^system dependencies@>
+
+One of the extensions to standard \PASCAL\ that we shall deal with is the
+ability to move to a random place in a binary file; another is to
+determine the length of a binary file. If \.{DVIIMP} is being used
+with \PASCAL s for which random file positioning is not efficiently
+available, the following definition should be changed from |true| to
+|false|; in such cases, \.{DVIIMP} will not include the optional feature
+that reads the postamble first.
+
+Another extension is to use a default |case| as in \.{TANGLE}, \.{WEAVE},
+etc.
+
+@d random_reading==true {should we skip around in the file?}
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The binary input comes from |dvi_file|, and the symbolic output is written
+on \PASCAL's standard |output| file. The term |print| is used instead of
+|write| when this program writes on |output|, so that all such output
+could easily be redirected if desired.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+@d print_nl==write_ln
+
+@p program DVI_IMP(@!dvi_file,@!im_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ jj:real; {a real variable}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ If the program has to stop prematurely, it goes to the
+`|final_end|'. Another label, |done|, is used when stopping normally.
+
+@d final_end=9999 {label for the end of it all}
+@d done=30 {go here when finished with a subtask}
+@d restart=40 {go here to restart an operation}
+
+@<Labels...@>=final_end;
+
+@ The following parameters can be changed at compile time to extend or
+reduce \.{DVIIMP}'s capacity.
+
+@<Constants...@>=
+@!max_fonts=100; {maximum number of distinct fonts per \.{DVI} file}
+@!max_glyphs=7680; {maximum number of different characters among all fonts}
+@!line_length=320; {bracketed lines of output will be at most this long}
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!stack_size=200; {\.{DVI} files shouldn't |push| beyond this depth}
+@!name_size=1000; {total length of all font file names}
+@!name_length=50; {a file name shouldn't be longer than this}
+@!m1_max=3; {max first |mm_store| index}
+@!m2_size=86000; {used as multiplier or divider}
+@!m2_max= 85999; {max second |mm_store| index}
+@!mm_size=344000; {bytes in |mm_store|}
+@!mm_max= 343999; {max location in |mm_store|}
+@!max_char_no=255; {largest allowed char number}
+
+@ Here are some macros for common programming idioms. We will have occasion,
+both in the |do_page| and the |do_char| routines, to group certain cases
+together and so we will also define these groupings at this time.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d do_nothing == {empty statement}
+@d unity == @'200000 {$2^{16}$, represents 1.00000}
+@d three_cases(#)==#,#+1,#+2
+@d four_cases(#)==#,#+1,#+2,#+3
+@d eight_cases(#)==four_cases(#),four_cases(#+4)
+@d nine_cases(#)==eight_cases(#),#+8
+@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
+@d nineteen_cases(#)==nine_cases(#),nine_cases(#+9),#+18
+@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
+@d thirty_seven_cases(#)==thirty_two_cases(#),four_cases(#+32),#+36
+@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)
+@d eighty_three_cases(#)==sixty_four_cases(#),nineteen_cases(#+64)
+@d one_sixty_five_cases(#)==
+ sixty_four_cases(#), sixty_four_cases(#+64),
+ thirty_seven_cases(#+128)
+
+@ If the \.{DVI} file is badly malformed, the whole process must be aborted;
+\.{DVIIMP} will give up, after issuing an error message about the symptoms
+that were noticed.
+
+Such errors might be discovered inside of subroutines inside of subroutines,
+so a procedure called |jump_out| has been introduced. This procedure, which
+simply transfers control to the label |final_end| at the end of the program,
+contains the only non-local |goto| statement in \.{DVIIMP}.
+@^system dependencies@>
+
+@d abort(#)==begin print(' ',#); jump_out;
+ end
+@d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
+@.Bad DVI file@>
+
+@p procedure jump_out;
+begin goto final_end;
+end;
+
+@* The character set.
+Like all programs written with the \.{WEB} system, \.{DVIIMP} can be
+used with any character set. But it uses ASCII code internally, because
+the programming for portable input-output is easier when a fixed internal
+code is used, and because \.{DVI} files use ASCII code for file names
+and certain other strings.
+
+The next few sections of \.{DVIIMP} have therefore been copied from the
+analogous ones in the \.{WEB} system routines. They have been considerably
+simplified, since \.{DVIIMP} need not deal with the controversial
+ASCII codes less than @'40. If such codes appear in the \.{DVI} file,
+they will be printed as question marks.
+
+@<Types...@>=
+@!ASCII_code=" ".."~"; {a subrange of the integers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lower case
+letters. Nowadays, of course, we need to deal with both upper and lower case
+alphabets in a convenient way, especially in a program like \.{DVIIMP}.
+So we shall assume that the \PASCAL\ system being used for \.{DVIIMP}
+has a character set containing at least the standard visible characters
+of ASCII code (|"!"| through |"~"|).
+
+Some \PASCAL\ compilers use the original name |char| for the data type
+associated with the characters in text files, while other \PASCAL s
+consider |char| to be a 64-element subrange of a larger data type that has
+some other name. In order to accommodate this difference, we shall use
+the name |text_char| to stand for the data type of the characters in the
+output file. We shall also assume that |text_char| consists of
+the elements |chr(first_text_char)| through |chr(last_text_char)|,
+inclusive. The following definitions should be adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ The \.{DVIIMP} processor converts between ASCII code and
+the user's external character set by means of arrays |xord| and |xchr|
+that are analogous to \PASCAL's |ord| and |chr| functions.
+
+@<Globals...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [0..255] of text_char;
+ {specifies conversion of output characters}
+
+@ Under our assumption that the visible characters of standard ASCII are
+all present, the following assignment statements initialize the
+|xchr| array properly, without needing any system-dependent changes.
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:='?';
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';
+for i:=@'177 to 255 do xchr[i]:='?';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
+for i:=" " to "~" do xord[xchr[i]]:=i;
+
+@* Device-independent file format.
+Before we get into the details of \.{DVIIMP}, we need to know exactly
+what \.{DVI} files are. The form of such files was designed by David R.
+@^Fuchs, David Raymond@>
+Fuchs in 1979. Almost any reasonable typesetting device can be driven by
+a program that takes \.{DVI} files as input, and dozens of such
+\.{DVI}-to-whatever programs have been written. Thus, it is possible to
+print the output of document compilers like \TeX\ on many different kinds
+of equipment.
+
+A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
+series of commands in a machine-like language. The first byte of each command
+is the operation code, and this code is followed by zero or more bytes
+that provide parameters to the command. The parameters themselves may consist
+of several consecutive bytes; for example, the `|set_rule|' command has two
+parameters, each of which is four bytes long. Parameters are usually
+regarded as nonnegative integers; but four-byte-long parameters,
+and shorter parameters that denote distances, can be
+either positive or negative. Such parameters are given in two's complement
+notation. For example, a two-byte-long distance parameter has a value between
+$-2^{15}$ and $2^{15}-1$.
+@.DVI {\rm files}@>
+
+A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
+or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that define the dimensions used in the
+file; this must come first. Each ``page'' consists of a |bop| command,
+followed by any number of other commands that tell where characters are to
+be placed on a physical page, followed by an |eop| command. The pages
+appear in the order that they were generated, not in any particular
+numerical order. If we ignore |nop| commands and \\{fnt\_def} commands
+(which are allowed between any two commands in the file), each |eop|
+command is immediately followed by a |bop| command, or by a |post|
+command; in the latter case, there are no more pages in the file, and the
+remaining bytes form the postamble. Further details about the postamble
+will be explained later.
+
+Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first byte is number~0, then comes number~1, and so on. For example,
+one of the parameters of a |bop| command points to the previous |bop|;
+this makes it feasible to read the pages in backwards order, in case the
+results are being directed to a device that stacks its output face up.
+Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
+first page occupies bytes 100 to 999, say, and if the second
+page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
+points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
+very first |bop|, i.e., the one that starts in byte 100, has a pointer of $-1$.)
+
+@ The \.{DVI} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+implicit instead of explicit. When a \.{DVI}-reading program reads the
+commands for a page, it keeps track of several quantities: (a)~The current
+font |f| is an integer; this value is changed only
+by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
+is given by two numbers called the horizontal and vertical coordinates,
+|h| and |v|. Both coordinates are zero at the upper left corner of the page;
+moving to the right corresponds to increasing the horizontal coordinate, and
+moving down corresponds to increasing the vertical coordinate. Thus, the
+coordinates are essentially Cartesian, except that vertical directions are
+flipped; the Cartesian version of |(h,v)| would be |(h,-v)|. (c)~The
+current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
+where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
+are used for vertical spacing. (d)~There is a stack containing
+|(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to
+change the current level of operation. Note that the current font~|f| is
+not pushed and popped; the stack contains only information about
+positioning.
+
+The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
+to 32 bits, including the sign. Since they represent physical distances,
+there is a small unit of measurement such that increasing |h| by~1 means
+moving a certain tiny distance to the right. The actual unit of
+measurement is variable, as explained below.
+
+@ Here is a list of all the commands that may appear in a \.{DVI} file. Each
+command is specified by its symbolic name (e.g., |bop|), its opcode byte
+(e.g., 139), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|p[4]|' means that parameter |p| is four bytes long. (A somewhat
+similar set of commands is used in \.{GF} files, as will be
+explained in a later section).
+
+\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
+such that the reference point of the character is at |(h,v)|. Then
+increase |h| by the width of that character. Note that a character may
+have zero or negative width, so one cannot be sure that |h| will advance
+after this command; but |h| usually does increase.
+
+\yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127).
+Do the operations of |set_char_0|; but use the character whose number
+matches the opcode, instead of character~0.
+
+\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
+number~|c| is typeset. \TeX82 uses this command for characters in the
+range |128<=c<256|.
+
+\yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
+bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this
+command, which is intended for processors that deal with oriental languages;
+but \.{DVIIMP} will allow character codes greater than 255, assuming that
+they all have the same width as the character whose code is $c \bmod 256$.
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+\yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
+bytes long, so it can be as large as $2^{24}-1$.
+
+\yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
+bytes long, possibly even negative. Imagine that.
+
+\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
+of height |a| and width |b|, with its bottom left corner at |(h,v)|. Then
+set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
+that if |b<0|, the value of |h| will decrease even though nothing else happens.
+Programs that typeset from \.{DVI} files should be careful to make the rules
+line up carefully with digitized characters, as explained in connection with
+the |rule_pixels| subroutine below.
+
+\yskip\hang|put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
+such that the reference point of the character is at |(h,v)|. (The `put'
+commands are exactly like the `set' commands, except that they simply put out a
+character or a rule without moving the reference point afterwards.)
+
+\yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
+
+\yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
+
+\yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
+
+\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
+|h| is not changed.
+
+\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
+may occur between \.{DVI} commands, but a |nop| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
+of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
+the current font |f| to an undefined value. The ten $c_i$ parameters can
+be used to identify pages, if a user wants to print only part of a \.{DVI}
+file; \TeX82 gives them the values of \.{\\count0} $\ldots$ \.{\\count9}
+at the time \.{\\shipout} was invoked for this page. The parameter |p|
+points to the previous |bop| command in the file, where the first |bop|
+has $p=-1$.
+
+\yskip\hang|eop| 140. End of page: Print what you have read since the
+previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
+programs that drive most output devices will have kept a buffer of the
+material that appears on the page that has just ended. This material is
+largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
+|h|~coordinate; so it usually needs to be sorted into some order that is
+appropriate for the device in question. \.{DVIIMP} does not do such sorting.)
+
+\yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the
+top of the stack; do not change any of these values. Note that |f| is
+not pushed.
+
+\yskip\hang|pop| 142. Pop the top six values off of the stack and assign
+them to |(h,v,w,x,y,z)|. The number of pops should never exceed the number
+of pushes, since it would be highly embarrassing if the stack were empty
+at the time of a |pop| command.
+
+\yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
+The parameter is a signed number in two's complement notation, |-128<=b<128|;
+if |b<0|, the reference point actually moves left.
+
+\yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a
+two-byte quantity in the range |-32768<=b<32768|.
+
+\yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a
+three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a
+four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|w| gets particular values.
+
+\yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |w|~spacing and moves right by |b|.
+
+\yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is a two-byte-long
+parameter, |-32768<=b<32768|.
+
+\yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is a three-byte-long
+parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is a four-byte-long
+parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
+commands are like the `|w|' commands except that they involve |x| instead
+of |w|.
+
+\yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |x|~spacing and moves right by |b|.
+
+\yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is a two-byte-long
+parameter, |-32768<=b<32768|.
+
+\yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is a three-byte-long
+parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is a four-byte-long
+parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
+The parameter is a signed number in two's complement notation, |-128<=a<128|;
+if |a<0|, the reference point actually moves up.
+
+\yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a
+two-byte quantity in the range |-32768<=a<32768|.
+
+\yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a
+three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a
+four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|y| gets particular values.
+
+\yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |y|~spacing and moves down by |a|.
+
+\yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is a two-byte-long
+parameter, |-32768<=a<32768|.
+
+\yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is a three-byte-long
+parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is a four-byte-long
+parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
+are like the `|y|' commands except that they involve |z| instead of |y|.
+
+\yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |z|~spacing and moves down by |a|.
+
+\yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is a two-byte-long
+parameter, |-32768<=a<32768|.
+
+\yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is a three-byte-long
+parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is a four-byte-long
+parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
+defined by a \\{fnt\_def} instruction, as explained below.
+
+\yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set
+|f:=1|, \dots, |f:=63|, respectively.
+
+\yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
+numbers in the range |64<=k<256|.
+
+\yskip\hang|fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
+bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
+command, but large font numbers may prove useful for specifications of
+color or texture, or they may be used for special fonts that have fixed
+numbers in some external coding scheme.
+
+\yskip\hang|fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
+bytes long, so it can be as large as $2^{24}-1$.
+
+\yskip\hang|fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
+bytes long; this is for the really big font numbers (and for the negative ones).
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
+programs are being used. \TeX82 generates |xxx1| when a short enough
+\.{\\special} appears, setting |k| to the number of bytes being sent. It
+is recommended that |x| be a string having the form of a keyword followed
+by possible parameters relevant to that keyword.
+
+\yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
+large. \TeX82 uses |xxx4| when |xxx1| would be incorrect.
+
+\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<256|; font definitions will be explained shortly.
+
+\yskip\hang|fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<65536|.
+
+\yskip\hang|fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
+
+\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@ @d set_char_0=0 {typeset character 0 and move right}
+@d set1=128 {typeset a character and move right}
+@d set_rule=132 {typeset a rule and move right}
+@d put1=133 {typeset a character}
+@d put_rule=137 {typeset a rule}
+@d nop=138 {no operation}
+@d bop=139 {beginning of page}
+@d eop=140 {ending of page}
+@d push=141 {save the current positions}
+@d pop=142 {restore previous positions}
+@d right1=143 {move right}
+@d w0=147 {move right by |w|}
+@d w1=148 {move right and set |w|}
+@d x0=152 {move right by |x|}
+@d x1=153 {move right and set |x|}
+@d down1=157 {move down}
+@d y0=161 {move down by |y|}
+@d y1=162 {move down and set |y|}
+@d z0=166 {move down by |z|}
+@d z1=167 {move down and set |z|}
+@d fnt_num_0=171 {set current font to 0}
+@d fnt1=235 {set current font}
+@d xxx1=239 {extension to \.{DVI} primitives}
+@d xxx4=242 {potentially long extension to \.{DVI} primitives}
+@d fnt_def1=243 {define the meaning of a font number}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+@d post_post=249 {postamble ending}
+@d undefined_commands==250,251,252,253,254,255
+
+@ The preamble contains basic information about the file as a whole. As
+stated above, there are six parameters:
+$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
+The |i| byte identifies \.{DVI} format; currently this byte is always set
+to~2. (Some day we will set |i=3|, when \.{DVI} format makes another
+incompatible change---perhaps in 1992.)
+
+The next two parameters, |num| and |den|, are positive integers that define
+the units of measurement; they are the numerator and denominator of a
+fraction by which all dimensions in the \.{DVI} file could be multiplied
+in order to get lengths in units of $10^{-7}$ meters. (For example, there are
+exactly 7227 \TeX\ points in 254 centimeters, and \TeX82 works with scaled
+points where there are $2^{16}$ sp in a point, so \TeX82 sets |num=25400000|
+and $|den|=7227\cdot2^{16}=473628672$.)
+@^sp@>
+
+The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the
+desired magnification. The actual fraction by which dimensions are
+multiplied is therefore $mn/1000d$. Note that if a \TeX\ source document
+does not call for any `\.{true}' dimensions, and if you change it only by
+specifying a different \.{\\mag} setting, the \.{DVI} file that \TeX\
+creates will be completely unchanged except for the value of |mag| in the
+preamble and postamble. (Fancy \.{DVI}-reading programs allow users to
+override the |mag|~setting when a \.{DVI} file is being printed.)
+
+Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
+interpreted further. The length of comment |x| is |k|, where |0<=k<256|.
+
+@d id_byte=2 {identifies the kind of \.{DVI} files described here}
+
+@ Font definitions for a given font number |k| contain further parameters
+$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
+The four-byte value |c| is the check sum that \TeX\ (or whatever program
+generated the \.{DVI} file) found in the \.{GF} file for this font;
+|c| should match the check sum of the font found by programs that read
+this \.{DVI} file.
+@^check sum@>
+
+Parameter |s| contains a fixed-point scale factor that is applied to the
+character widths in font |k|; font dimensions in \.{GF} files and other
+font files are relative to this quantity, which is always positive and
+less than $2^{27}$. It is given in the same units as the other dimensions
+of the \.{DVI} file. Parameter |d| is similar to |s|; it is the ``design
+size,'' and it is given in \.{DVI} units that have not been corrected for
+the magnification~|mag| found in the preamble. Thus, font |k| is to be
+used at $|mag|\cdot s/1000d$ times its normal size.
+
+The remaining part of a font definition gives the external name of the font,
+which is an ASCII string of length |a+l|. The number |a| is the length
+of the ``area'' or directory, and |l| is the length of the font name itself;
+the standard local system font area is supposed to be used when |a=0|.
+The |n| field contains the area in its first |a| bytes.
+
+Font definitions must appear before the first use of a particular font number.
+Once font |k| is defined, it must not be defined again; however, we
+shall see below that font definitions appear in the postamble as well as
+in the pages, so in this sense each font number is defined exactly twice,
+if at all. Like |nop| commands and \\{xxx} commands, font definitions can
+appear before the first |bop|, or between an |eop| and a |bop|.
+
+@ The last page in a \.{DVI} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \TeX\ has
+accumulated about the file, making it possible to print subsets of the data
+with reasonable efficiency. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
+ $\langle\,$font definitions$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the final |bop| in the file. The next three
+parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
+appeared in the preamble.
+
+Parameters |l| and |u| give respectively the height-plus-depth of the tallest
+page and the width of the widest page, in the same units as other dimensions
+of the file. These numbers might be used by a \.{DVI}-reading program to
+position individual ``pages'' on large sheets of film or paper.
+
+Parameter |s| is the maximum stack depth (i.e., the largest excess of
+|push| commands over |pop| commands) needed to process this file. Then
+comes |t|, the total number of pages (|bop| commands) present.
+
+The postamble continues with font definitions, which are any number of
+\\{fnt\_def} commands as described above, possibly interspersed with |nop|
+commands. Each font number that is used in the \.{DVI} file must be defined
+exactly twice: Once before it is first selected by a \\{fnt} command, and once
+in the postamble.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the font definitions, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~2, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{DVI} file makes it feasible for
+\.{DVI}-reading programs to find the postamble first, on most computers,
+even though \TeX\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{DVI} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{DVI} reader discovers all the information needed for typesetting the
+pages. Note that it is also possible to skip through the \.{DVI} file at
+reasonably high speed to locate a particular page, if that proves
+desirable. This saves a lot of time, since \.{DVI} files used in production
+jobs tend to be large.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{DVI}
+format has been designed to work most efficiently with modern operating systems.
+As noted above, \.{DVIIMP} will limit itself to the restrictions of standard
+\PASCAL\ if |random_reading| is defined to be |false|.
+
+@* The imPRESS file format.
+The format of an \.{imPRESS} file is quite similar in many ways to the
+format of \.{DVI} files although, of course, the commands are all related
+to the specific properties of the \.{IMAGEN} printer. For example,
+dimensions are all in units that are derived from the inter-pixel distance
+for the printer that is being used (1/300 of an inch on a 300
+pixels-per-inch printer). As far as we are concerned, an \.{imPRESS} file
+consists of a sequence of bytes although, for some instructions the
+associated parameters are made up of a collection of bits that are packed,
+rather arbitrarily,
+into one or more complete bytes (the commands themselves are never
+split between bytes).
+
+As will be explained in more detail later, the \.{IMAGEN} printer provides
+facilities for defining certain state variables and for saving and
+restoring sets of these variable through the use of push and pop commands.
+
+The Imagen Corporation provides a publication-form-name that is used for
+describing the commands and we will, so far as practical, use modified
+forms of these publification-form-namess as our names for these commands,
+simply prefacing the \.{IMAGEN} command name with \.{im} when this can be done
+without making the name too long.
+For consistancy, the same conventions are used to
+specify the parameters as were used in module 15.
+
+For the reader's convenience, we will list these commands under the same
+headings as used in the \.{imPRESS} Programmer's Manual.
+
+Document Structure Commands
+
+\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
+such that the reference point of the character is at |(h,v)|. Then
+increase |h| by the width of that character. Note that a character may
+have zero or negative width, so one cannot be sure that |h| will advance
+after this command; but |h| usually does increase.
+
+\yskip\hang|im_end_page| 219. This command declares the current page ready
+for printing and starts page layout on a new page. State variables, which
+are set once and remain in effect until changed, remain unchanged. These
+include the current (|h|,|v|) position so these need to be reset as
+desired. Note that some manipulation of data may be needed between a
+\.{DVI} |eop| and an \.{imPRESS} |im_end_page|.
+
+\yskip\hang|im_eof| 255. Marks the end of the \.{imPRESS} document. Any
+text after this command in the input file will be ignored.
+
+\yskip\hang|im_no_op| 254. May be used for padding and is ignored. May be
+used as a direct translation for \.{DVI}'s |nop|.
+
+Coordinate System Commands
+
+\yskip\hang|set_hv_system| 205 [1]. This command selects the logical
+coordinate that is to be used to lay out the pages. This command need not
+be given if the default coordinates are to be used (with |h| and |v| axes
+equivalent to those for |x| and |y|). The associated byte has a zero
+first bit, the next two bits specify the origin, the next two bits specify
+the axes and the final three bits specify the orientation.
+For details, see the \.{imPRESS} User's Manual.
+
+\yskip\hang|set_abs_h| 135 [2]. Set the |h| to the value given in the
+following 16-bit signed word.
+
+\yskip\hang|set_rel_h| 136 [2]. Add the value given in the following
+16-bit signed word to |h|,
+
+\yskip\hang|set_abs_v| 137 [2]. Set the |v| to the value given in the
+following 16-bit signed word.
+
+\yskip\hang|set_rel_v| 138 [2]. Add the value given in the following
+16-bit signed word to |v|,
+
+Text Positioning Commands
+
+\yskip\hang|im_page| 213. Set both |h| and |v| to zero.
+
+\yskip\hang|im_set_adv_dirs| 206 [1]. Set the main and secondary advance
+directions as specified in the following byte. The default direction
+corresponde to normal english usage.
+For details, see the \.{imPRESS} User's Manual.
+
+\yskip\hang|im_mmove| 133 [2]. Displace the current |h|,|v| position in the
+main advance direction by the value in the following signed 16notbit
+word. With the default value for |im_set_adv_dirs| this command is the
+same as |im_set_rel_h|.
+
+\yskip\hang|im_smove| 134 [2]. Displace the current |h|,|v| position in the
+secondary advance direction by the value in the following signed 16notbit
+word. With the default value for |im_set_adv_dirs| this command is the
+same as |im_set_rel_v|.
+
+\yskip\hang|im_set_sp| 210 [2]. Set the current inter-word spacing to
+the value in the following 16-bit signed word.
+We will not use this command as \TeX\ normally handles this matter.
+
+\yskip\hang|im_sp| 128. This command performs an inter-word space of the
+size specified by the |im_set_sp| command.
+We will not use this command as \TeX\ normally handles this matter.
+
+\yskip\hang|im_sp1| 129. This command performs an inter-word space of the
+size one pixel greater than that specified by the |im_set_sp| command.
+We will not use this command as \TeX\ normally handles this matter.
+
+\yskip\hang|im_mplus| 131. This command adjusts the current position by one
+pixel in the main advance direction, that is normally to add one to the
+current value of |h|.
+
+\yskip\hang|im_mminus| 132. This command adjusts the current position by
+minus one pixel in the main advance direction, that is normally to
+subtract one from the current value of |h|.
+
+\yskip\hang|im_crlf| 197. With no special advance directions, this command
+sets |h| to the beginning-of-line value and advances |v| by the inter-line
+space amount.
+
+\yskip\hang|im_set_bol| 209 [2]. Set the beginning-of-line margin to the
+value specified in the following signed 16-bit word.
+
+\yskip\hang|im_set_il| 208 [2]. Set the inter-line space to the value
+given in the following signed 16-bit word.
+
+Text Printing Commnds
+
+\yskip\hang|im_bgly| 199 [12 plus mask]. This command is used to download
+glyphs defined by two bytes specifying <rotation, family, and member>, and
+specified by two bytes each for the following four parameters,
+width, left-offset, height, and top-offset, and finally by a mask
+specifying the complete raster for the glyph within a minimum sized
+bounding box (padded at the right with enough empty (white) pixels to
+complete an otherwise partially filled byte). The rows are orderd starting
+with the top row. The number of bits for this mask is then |((width+7) div
+8)*height|. Once the rotation and family have been stated, a series of glyphs
+from this family may be printed by a string of bytes containing their member
+numbers.
+
+\yskip\hang|set_family| 207 [1]. This command sets the current-family to
+|family| which must lie in the range from 0 to 95.
+
+\yskip\hang|im_member| 0-127. An \.{imPRESS} command code in the range
+from 0 and 127 is a member command, calling for the designated member of
+the current family to be printed at the current position and for the
+printer to advance in the main advance direction by the glyph's associated
+advance-width value.
+
+Resident Glyphs
+
+Normally, we will make no use of the resident glyphs provided by the
+\.{IMAGEN} processor, since \TeX\ has no knowledge of these. These fonts
+are not accessed directly but must be referenced indirectly through member
+maps and family tables. For completeness, the commands that are used to
+create these maps and family tables are here listed. For details see the
+\.{imPRESS} User's Manual.
+
+\yskip\hang|create_map| 222
+
+\yskip\hang|create_family_table| 221.
+
+Text Rule Command
+
+\yskip\hang|im_brule| 193 w[2] h[2] t[2]. This command prints a rectangle
+(either in black or textured) of width w and height h with a top-offset
+of t where a positive value means below the current position.
+
+State Saving and Restoring
+
+\yskip\hang|set_push_mask| 214 [2]. This command specifies which of the
+various state variables are to be saved. Nine variables, set by the last 9
+bits (with the first 7 bits set to zero) of the associated 16-bit word are
+involved, these being: pen-and-texture, interword-space,
+beginning-of-line, family, hv-position, advance-direction, origin, and
+orientation. These are all marked for saving (set to one) at the beginning
+of each document and remain so unless changed by this command.
+
+\yskip\hang|im_push| 211. Save the state variables as prespecified
+originally or as altered by the |set_push_mask| command.
+
+\yskip\hang|im_pop| 212. Restore the state variables saved by the most
+recent unmatched |im_push| command.
+
+
+@ @d im_sp=128 {advance one space}
+@d im_sp1=129 {advance one space plus 1 pixel}
+@d im_mplus=131 {advance one pixel}
+@d im_mminus=132 {back up one pixel}
+@d im_mmove=133 {move in the main advance direction}
+@d im_smove=134 {move in the secondary advance direction}
+@d set_abs_h=135 {move to |h| position}
+@d set_rel_h=136 {move in the |h| direction}
+@d set_abs_v=137 {move to |v| position}
+@d set_rel_v=138 {move in the |v| direction}
+@d circ_arc=150 {define a circular path}
+@d ellipse_arc=151 {define an eliptical path}
+@d circ_segm=160 {define a pie-shaped path}
+@d im_brule=193 {print a rule}
+@d im_crlf=197 {move to the beginning of th next line}
+@d im_bgly=199 {define a downloaded glyph}
+@d set_hv_system=205 {select a logical coordinate system}
+@d im_set_adv_dirs=206 {set the advance directions}
+@d set_family=207 {set current-family to family}
+@d im_set_il=208 {set inter-line spacing}
+@d im_set_bol=209 {set margin}
+@d im_set_sp=210 {set inter-word spacing}
+@d im_push=211 {save the state variables}
+@d im_pop=212 {restore the state variables}
+@d im_page=213 {set both |h| and |v| to zero}
+@d set_push_mask=214 {specify variables to save}
+@d im_end_page=219 {end the page}
+@d create_family_table=221 {define a family table}
+@d create_map=222 {create a member map}
+@d set_pum=225 {append new path or replace path}
+@d create_path=230 {define a path of segments}
+@d set_texture=231 {select a texture for drawing}
+@d set_pen=232 {select a pen width (in pixels)}
+@d fill_path=233 {shade the ares inside the path}
+@d draw_path=234 {draw the current path (a line)}
+@d bitmap=235 {print a full bitmap}
+@d set_magnification=236 {magnify the page (by 1, 2, or 4)}
+@d define_macro=242 {define a macro}
+@d execute_macro=243 {execute the named macro}
+@d im_no_op=254 {no operation}
+@d im_eof=255 {end the document}
+
+
+@* Input and Output for binary files.
+We have seen that a \.{DVI} file is a sequence of 8-bit bytes. The bytes
+appear physically in what is called a `|packed file of 0..255|'
+in \PASCAL\ lingo.
+
+Packing is system dependent, and many \PASCAL\ systems fail to implement
+such files in a sensible way (at least, from the viewpoint of producing
+good production software). For example, some systems treat all
+byte-oriented files as text, looking for end-of-line marks and such
+things. Therefore some system-dependent code is often needed to deal with
+binary files, even though most of the program in this section of
+\.{DVIIMP} is written in standard \PASCAL.
+@^system dependencies@>
+
+One common way to solve the problem is to consider files of |integer|
+numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to
+a sequence of four bytes $(a,b,c,d)$ using the following code, which
+avoids the controversial integer division of negative numbers:
+$$\vbox{\halign{#\hfil\cr
+|if x>=0 then a:=x div @'100000000|\cr
+|else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr
+\quad|end|\cr
+|x:=x mod @'100000000;|\cr
+|b:=x div @'200000; x:=x mod @'200000;|\cr
+|c:=x div @'400; d:=x mod @'400;|\cr}}$$
+The four bytes are then kept in a buffer and output one by one. (On 36-bit
+computers, an additional division by 16 is necessary at the beginning.
+Another way to separate an integer into four bytes is to use/abuse
+\PASCAL's variant records, storing an integer and retrieving bytes that are
+packed in the same place; {\sl caveat implementor!\/}) It is also desirable
+in some cases to read a hundred or so integers at a time, maintaining a
+larger buffer.
+
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+
+@ The program deals with four binary file variables: |dvi_file| is the main
+input file that we are translating into symbolic form, |gf_file| is
+the generic font file from which the font information is being read,
+|tfm_file| is the font-metric file that is used for width information
+in those cases where this information is available but the corresponding
+|gf_file| is not, and
+|im_file| is the output file that is to be sent to the \.{IMAGEN} printer.
+
+@<Glob...@>=
+@!dvi_file:byte_file; {the stuff we are transcribing to the IMAGEN}
+@!gf_file:byte_file; {a generic font file}
+@!tfm_file:byte_file; {a generic font file}
+@!im_file:byte_file; {the output file}
+
+@ Special considerations are involved in restricting the range of pages
+that one may want to print since the |count[0]| numbers that the |dvi|
+file reports may be either positive (the usual case) or negative to signal
+that the page numbers are to be printed in italics. We will assume that the
+user will specify, 1) the |count[0]| number of the first page that he or
+she will wants printed, by typing `/f' followed by this number, and 2) the
+tolal number of pages to be printed (in the order that they occur in the
+\.{DVI} file), by typing `/n' again followed by the number wanted. The
+\.{Imagen} will, of course, actually deliver these pages in reverse order.
+The method of reading these numbers is system dependent and the procedures
+for calling |read_f|, etc., will be found in the change file.
+Note that only |count[0]| will be used (as defined
+in \.{PLAIN} and the remaining |count| numbers will be ignored.
+
+@<Glob...@>=
+@!start_page:integer; {the requested starting page |count[0]| number}
+@!num_pages:integer; {the requested number of pages to be printed}
+@!f_count,l_count:integer; {backward counts used when printing partial file}
+@!f_flag,n_flag:boolean; {true when /f and /n are specified}
+@!page_match:boolean; {true when starting page is found}
+@!counter:integer; {used in back-counting pages}
+@!copies:integer; {the number of copies requested}
+
+@ @<Set init...@>=
+counter:=0; f_count:=max_pages; l_count:=0;
+num_pages:=max_pages; copies:=1;
+f_flag:=false; n_flag:=false;
+resolution:=300.0;
+h_org:=round(resolution); v_org:=round(resolution);
+
+@ The following procedures will be needed.
+
+@p function read_int:integer;
+var i:integer;
+@!neg_flag:boolean;
+begin
+neg_flag:=false; i:=0;
+get(tty);
+while tty^=' ' do get(tty);
+if (tty^='-') then neg_flag:=true;
+while (tty^='-') or (tty^='+') do get(tty);
+while (tty^>='0') and (tty^<='9') do begin
+ i:=i*10+xord[tty^]-"0"; get(tty);
+ end;
+if neg_flag then i:=-i;
+read_int:=i;
+end;
+@#
+procedure read_f;
+begin
+start_page:=read_int;
+f_flag:=true;
+end;
+@#
+procedure read_n;
+begin
+num_pages:=read_int;
+if num_pages=0 then num_pages:=max_pages;
+n_flag:=true;
+end;
+@#
+procedure read_c;
+begin
+copies:=read_int;
+end;
+@#
+procedure read_h;
+begin
+h_org:=read_int;
+end;
+@#
+procedure read_v;
+begin
+v_org:=read_int;
+end;
+
+@ To prepare the input files, we |reset| them. An extension of
+\PASCAL\ is needed in the case of |gf_file| and of |tfm_file|,
+since we want to associate them
+with external files whose names are specified dynamically (i.e., not
+known at compile time). The following code assumes that `|reset(f,s)|'
+does this, when |f| is a file variable and |s| is a string variable that
+specifies the file name. If |eof(f)| is true immediately after
+|reset(f,s)| has acted, we assume that no file named |s| is accessible.
+@^system dependencies@>
+
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin reset(dvi_file);
+cur_loc:=0;
+end;
+@#
+procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file,cur_name);
+cur_gf_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin reset(tfm_file,cur_tfm_name);
+end;
+
+@ To prepare the |im_file| for output, we |rewrite| it.
+
+@p procedure open_im_file; {prepares to write packed bytes in |im_file|}
+begin rewrite(im_file); im_byte_no:=0;
+end;
+
+@ If you looked carefully at the preceding code, you probably asked,
+``What are |cur_loc| and |cur_name|?'' Good question. They're global
+variables: |cur_loc| is the number of the byte about to be read next from
+|dvi_file|, and |cur_name| is a string variable that will be set to the
+generic font file name before |open_gf_file| is called. While we are at
+it, we will also declare |cur_gf_loc|.
+
+@<Glob...@>=
+@!cur_loc:integer; {where we are about to look, in |dvi_file|}
+@!cur_gf_loc:integer; {where we are about to look, in |gf_file|}
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+@!cur_tfm_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+@!im_byte_no:integer; {where we are about to write, in |im_file|}
+
+@ We shall use a set of simple functions to read the next byte or bytes
+from a |gf_file|.
+@^system dependencies@>
+
+@p function gf_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then gf_byte:=0
+else begin read(gf_file,b); incr(cur_gf_loc); gf_byte:=b;
+ end;
+end;
+@#
+function gf_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(gf_file,a); read(gf_file,b);
+cur_gf_loc:=cur_gf_loc+2;
+gf_two_bytes:=a*256+b;
+end;
+@#
+function gf_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c);
+cur_gf_loc:=cur_gf_loc+3;
+gf_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function gf_signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+cur_gf_loc:=cur_gf_loc+4;
+if a<128 then gf_signed_quad:=((a*256+b)*256+c)*256+d
+else gf_signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+
+@ We will refer to \.{TFM} files for character width information in those
+cases where \.{.GF} files are not available. We read four bytes at a
+time, putting the input into global
+variables |b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and
+|b3| the fourth.
+
+@<Glob...@>=
+@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once}
+
+@ The |read_tfm_word| procedure sets |b0| through |b3| to the next
+four bytes in the current \.{TFM} file.
+@^system dependencies@>
+
+@p procedure read_tfm_word;
+begin read(tfm_file,b0); read(tfm_file,b1);
+read(tfm_file,b2); read(tfm_file,b3);
+end;
+
+@ We shall use another set of simple functions to read the next byte or
+bytes from |dvi_file|. There are seven possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(dvi_file) then get_byte:=0
+else begin read(dvi_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:eight_bits;
+begin read(dvi_file,b); incr(cur_loc);
+if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function signed_pair:integer; {returns the next two bytes, signed}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+if a<128 then signed_pair:=a*256+b
+else signed_pair:=(a-256)*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_trio:integer; {returns the next three bytes, signed}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+if a<128 then signed_trio:=(a*256+b)*256+c
+else signed_trio:=((a-256)*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+
+@ Finally we come to the routines that are used only if |random_reading| is
+|true|. The driver program below needs two such routines: |dvi_length| should
+compute the total number of bytes in |dvi_file|, possibly also
+causing |eof(dvi_file)| to be true; and |move_to_byte(n)|
+should position |dvi_file| so that the next |get_byte| will read byte |n|,
+starting with |n=0| for the first byte in the file.
+@^system dependencies@>
+
+Such routines are, of course, highly system dependent. They are implemented
+here in terms of two assumed system routines called |set_pos| and |cur_pos|.
+The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is
+negative or larger than the total number of items in |f|; in the latter
+case, |set_pos(f,n)| moves to the end of file |f|.
+The call |cur_pos(f)| gives the total number of items in |f|, if
+|eof(f)| is true; we use |cur_pos| only in such a situation.
+
+@p function dvi_length:integer;
+begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file);
+end;
+@#
+procedure move_to_byte(n:integer);
+begin set_pos(dvi_file,n); cur_loc:=n;
+end;
+
+@ We face a similar problem in dealing with the \.{GF} files so perhaps we
+should deal with this problem at this time. We will need two special
+routines, one to determine the byte length of the individual \.{GF} files
+and the second to position |gf_file| so that the next |gf_byte| will read
+byte |n|, starting with |n=0| for the first byte in the file.
+@^system dependencies@>
+
+@p function gf_length:integer;
+begin set_pos(gf_file,-1); gf_length:=cur_pos(gf_file);
+end;
+@#
+procedure move_to_gf_byte(n:integer);
+begin set_pos(gf_file,n); cur_gf_loc:=n;
+end;
+
+@ We will also need a simple way of sending bytes, unsigned bytes, and signed
+16-bit words to the |im_file|. While the \.{imPRESS} manual user |u_byte|
+for an unsigned byte, we will attach an `s' prefix for the signed case, leaving
+|im_byte| to mean an unsigned byte as used elsewhere in this program.
+
+@d im_byte(#)==begin write(im_file,#);
+ incr(im_byte_no); end
+
+@p procedure im_sbyte(@!w:integer);
+begin
+if w<0 then w:=w+@"100;
+im_byte(w);
+end;
+@#
+procedure im_halfword(@!w:integer);
+begin
+if w<0 then w:=w+@"10000;
+im_byte(w div @"100);
+im_byte(w mod @"100);
+end;
+
+@* GF file format.
+This program, in contrast with many device drivers, gets its font
+information directly from the ``generic font'' (\.{GF}) files that are the
+most important output produced by the \MF\ program. The term {\sl
+generic\/} indicates that this file format doesn't match the conventions
+of any name-brand manufacturer; but it is easy to convert \.{GF} files to
+the special format required by almost all digital phototypesetting
+equipment, if these devices are designed to accept fonts directly.
+Alternately, one can translate the \.{GF} and pass the needed raster
+information on to the printer at the time that a \.{DVI} file is being
+processed, as is done in this program.
+
+There's a strong analogy
+between the \.{DVI} files written by \TeX\ and the \.{GF} files written
+by \MF; and, in fact, the file formats have a lot in common.
+
+A \.{GF} file is a stream of 8-bit bytes that may be
+regarded as a series of commands in a machine-like language. The first
+byte of each command is the operation code, and this code is followed by
+zero or more bytes that provide parameters to the command. The parameters
+themselves may consist of several consecutive bytes; for example, the
+`|boc|' (beginning of character) command has six parameters, each of
+which is four bytes long, while the shortened, more ofter used, form, `|boc1|'
+has five parameters, each of which is only one byte long.
+Parameters are usually regarded as nonnegative
+integers; but four-byte-long parameters can be either positive or
+negative, hence they range in value from $-2^{31}$ to $2^{31}-1$.
+As in \.{TFM} files, numbers that occupy
+more than one byte position appear in BigEndian order,
+and negative numbers appear in two's complement notation.
+
+A \.{GF} file consists of a ``preamble,'' followed by a sequence of one or
+more ``characters,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that introduce the file; this must come
+first. Each ``character'' consists of a |boc| or a |boc1|
+command, followed by any
+number of other commands that specify ``black'' pixels,
+followed by an |eoc| command. The characters appear in the order that \MF\
+generated them. If we ignore no-op commands (which are allowed between any
+two commands in the file), each |eoc| command is immediately followed by a
+|boc| or a |boc1|
+command, or by a |post| command; in the latter case, there are no
+more characters in the file, and the remaining bytes form the postamble.
+Further details about the postamble will be explained later.
+
+Some parameters in \.{GF} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first file byte is number~0, then comes number~1, and so on.
+
+@ The \.{GF} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+relative instead of absolute. When a \.{GF}-reading program reads the
+commands for a character, it keeps track of two quantities: (a)~the current
+column number,~|m|; and (b)~the current row number,~|n|. These are 32-bit
+signed integers, although most actual font formats produced from \.{GF}
+files will need to curtail this vast range because of practical
+limitations. (\MF\ output will never allow $\vert m\vert$ or $\vert
+n\vert$ to exceed 4096, but the \.{GF} format tries to be more general.)
+
+How do \.{GF}'s row and column numbers correspond to the conventions
+of \TeX\ and \MF? Well, the ``reference point'' of a character, in \TeX's
+view, is considered to be at the lower left corner of the pixel in row~0
+and column~0. This point is the intersection of the baseline with the left
+edge of the type; it corresponds to location $(0,0)$ in \MF\ programs.
+Thus the pixel in \.{GF} row~0 and column~0 is \MF's unit square, comprising the
+region of the plane whose coordinates both lie between 0 and~1. The
+pixel in \.{GF} row~|n| and column~|m| consists of the points whose \MF\
+coordinates |(x,y)| satisfy |m<=x<=m+1| and |n<=y<=n+1|. Negative values of
+|m| and~|x| correspond to columns of pixels {\sl left\/} of the reference
+point; negative values of |n| and~|y| correspond to rows of pixels {\sl
+below\/} the baseline.
+
+Besides |m| and |n|, there's also a third aspect of the current
+state, namely the @!|paint_switch|, which is always either \\{black} or
+\\{white}. Each \\{paint} command advances |m| by a specified amount~|d|,
+and blackens the intervening pixels if |paint_switch=black|; then
+the |paint_switch| changes to the opposite state. \.{GF}'s commands are
+designed so that |m| will never decrease within a row, and |n| will never
+increase within a character; hence there is no way to whiten a pixel that
+has been blackened. \.{DVIIMP} does not use a |paint_switch| parameter,
+as such, but other programs do and the concept is useful in following
+the way that the |paint| commands are handled.
+
+@ Here is a list of all the commands that may appear in a \.{GF} file. Each
+command is specified by its symbolic name (e.g., |boc|), its opcode byte
+(e.g., 67), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|d[2]|' means that parameter |d| is two bytes long.
+
+\yskip\hang|paint_0| 0. This is a \\{paint} command with |d=0|; it does
+nothing but change the |paint_switch| from \\{black} to \\{white} or vice~versa.
+
+\yskip\hang\\{paint\_1} through \\{paint\_63} (opcodes 1 to 63).
+These are \\{paint} commands with |d=1| to~63, defined as follows: If
+|paint_switch=black|, blacken |d|~pixels of the current row~|n|,
+in columns |m| through |m+d-1| inclusive. Then, in any case,
+complement the |paint_switch| and advance |m| by~|d|.
+
+\yskip\hang|paint1| 64 |d[1]|. This is a \\{paint} command with a specified
+value of~|d|; \MF\ uses it to paint when |64<=d<256|.
+
+\yskip\hang|@!paint2| 65 |d[2]|. Same as |paint1|, but |d|~can be as high
+as~65535.
+
+\yskip\hang|@!paint3| 66 |d[3]|. Same as |paint1|, but |d|~can be as high
+as $2^{24}-1$. \MF\ never needs this command, and it is hard to imagine
+anybody making practical use of it; surely a more compact encoding will be
+desirable when characters can be this large. But the command is there,
+anyway, just in case.
+
+\yskip\hang|boc| 67 |c[4]| |p[4]| |min_m[4]| |max_m[4]| |min_n[4]|
+|max_n[4]|. Beginning of a character: Here |c| is the character code, and
+|p| points to the previous character beginning (if any) for characters having
+this code number modulo 256. (The pointer |p| is |-1| if there was no
+prior character with an equivalent code.) The values of registers |m| and |n|
+defined by the instructions that follow for this character must
+satisfy |min_m<=m<=max_m| and |min_n<=n<=max_n|. (The values of |max_m| and
+|min_n| need not be the tightest bounds possible.) When a \.{GF}-reading
+program sees a |boc|, it can use |min_m|, |max_m|, |min_n|, and |max_n| to
+initialize the bounds of an array. Then it sets |m:=min_m|, |n:=max_n|, and
+|paint_switch:=white|.
+
+\yskip\hang|boc1| 68 |c[1]| |@!del_m[1]| |max_m[1]| |@!del_n[1]| |max_n[1]|.
+Same as |boc|, but |p| is assumed to be~$-1$; also |del_m=max_m-min_m|
+and |del_n=max_n-min_n| are given instead of |min_m| and |min_n|.
+The one-byte parameters must be between 0 and 255, inclusive.
+\ (This abbreviated |boc| saves 19~bytes per character, in common cases.)
+
+\yskip\hang|eoc| 69. End of character: All pixels blackened so far
+constitute the pattern for this character. In particular, a completely
+blank character might have |eoc| immediately following |boc|.
+
+\yskip\hang|skip0| 70. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=white|. \ (This finishes one row and begins another,
+ready to whiten the leftmost pixel in the new row.)
+
+\yskip\hang|skip1| 71 |d[1]|. Decrease |n| by |d+1|, set |m:=min_m|, and set
+|paint_switch:=white|. This is a way to produce |d| all-white rows.
+
+\yskip\hang|@!skip2| 72 |d[2]|. Same as |skip1|, but |d| can be as large
+as 65535.
+
+\yskip\hang|@!skip3| 73 |d[3]|. Same as |skip1|, but |d| can be as large
+as $2^{24}-1$. \MF\ obviously never needs this command.
+
+\yskip\hang|new_row_0| 74. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=black|. \ (This finishes one row and begins another,
+ready to {\sl blacken\/} the leftmost pixel in the new row.)
+
+\yskip\hang|@!new_row_1| through |@!new_row_164| (opcodes 75 to 238). Same as
+|new_row_0|, but with |m:=min_m+1| through |min_m+164|, respectively.
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |no_op| unless special \.{GF}-reading
+programs are being used. \MF\ generates \\{xxx} commands when encountering
+a \&{special} string; this occurs in the \.{GF} file only between
+characters, after the preamble, and before the postamble. However,
+\\{xxx} commands might appear anywhere in \.{GF} files generated by other
+processors. It is recommended that |x| be a string having the form of a
+keyword followed by possible parameters relevant to that keyword.
+
+\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+\MF\ uses this when sending a \&{special} string whose length exceeds~255.
+
+\yskip\hang|@!xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be
+ridiculously large; |k| mustn't be negative.
+
+\yskip\hang|yyy| 243 |y[4]|. This command is undefined in general;
+it functions as a 5-byte |no_op| unless special \.{GF}-reading programs
+are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a
+result of \&{numspecial} commands; the intent is to provide numeric
+parameters to \\{xxx} commands that immediately precede.
+
+\yskip\hang|no_op| 244. No operation, do nothing. Any number of |no_op|'s
+may occur between \.{GF} commands, but a |no_op| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|char_loc| 245 |c[1]| |dx[4]| |dy[4]| |w[4]| |p[4]|.
+This command will appear only in the postamble, which will be explained shortly.
+
+\yskip\hang|@!char_loc0| 246 |c[1]| |@!dm[1]| |w[4]| |p[4]|.
+Same as |char_loc|, except that |dy| is assumed to be zero, and the value
+of~|dx| is taken to be |65536*dm|, where |0<=dm<256|.
+
+\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameter |i| is an identifying number for \.{GF} format, currently
+131. The other information is merely commentary; it is not given
+special interpretation like \\{xxx} commands are. (Note that \\{xxx}
+commands may immediately follow the preamble, before the first |boc|.)
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@d gf_id_byte=131 {identifies the kind of \.{GF} files described here}
+
+@ Here are the opcodes that \.{DVIIMP} actually refers to.
+
+@d paint_0=0 {beginning of the \\{paint} commands}
+@d paint1=64 {move right a given number of columns, then
+ black${}\swap{}$white}
+@d paint2=65
+@d boc=67 {beginning of a character}
+@d boc1=68 {abbreviated |boc|}
+@d eoc=69 {end of a character}
+@d skip0=70 {skip no blank rows}
+@d skip1=71 {skip over blank rows}
+@d skip2=72 {skip over blank rows}
+@d new_row_0=74 {move down one row and then right}
+@d new_row_164=238 {move down 164 rows and then right}
+{xxx1=239 defined previously}
+@d yyy=243 {for \&{numspecial} numbers}
+@d no_op=244 {no operation}
+@d char_loc=245 {character locators in the postamble}
+{pre=247 (preamble) defined previously}
+{post 248 (postamble beginning) defined previously}
+{|post_post|=249 (postamble ending) defined previously}
+{undefined commands==250,251,252,253,254,255}
+
+@ The last character in a \.{GF} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \MF\ has
+accumulated. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |@!ds[4]| |@!cs[4]| |@!hppp[4]| |@!vppp[4]|
+ |min_m[4]| |max_m[4]| |min_n[4]| |max_n[4]|\cr
+ $\langle\,$character locators$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the byte following the final |eoc| in the file
+(or to the byte following the preamble, if there are no characters);
+it can be used to locate the beginning of \\{xxx} commands
+that might have preceded the postamble. The |ds| and |cs| parameters
+@^design size@> @^check sum@>
+give the design size and check sum, respectively, which are exactly the
+values put into the header of any \.{TFM} file that shares information with this
+\.{GF} file. Parameters |hppp| and |vppp| are the ratios of
+pixels per point, horizontally and vertically, expressed as |scaled| integers
+(i.e., multiplied by $2^{16}$); they can be used to correlate the font
+with specific device resolutions, magnifications, and ``at sizes.'' Then
+come |min_m|, |max_m|, |min_n|, and |max_n|, which bound the values that
+registers |m| and~|n| assume in all characters in this \.{GF} file.
+(These bounds need not be the best possible; |max_m| and |min_n| may, on the
+other hand, be tighter than the similar bounds in |boc| commands. For
+example, some character may have |min_n=-100| in its |boc|, but it might
+turn out that |n| never gets lower than |-50| in any character; then
+|min_n| can have any value |<=-50|. If there are no characters in the file,
+it's possible to have |min_m>max_m| and/or |min_n>max_n|.)
+
+@ Character locators are introduced by |char_loc| commands,
+which specify a character residue~|c|, character displacements (|dx,dy|),
+a character width~|w|, and a pointer~|p|
+to the beginning of that character. (If two or more characters have the
+same code~|c| modulo 256, only the last will be indicated; the others can be
+located by following backpointers. Characters whose codes differ by a
+multiple of 256 are assumed to share the same font metric information,
+hence the \.{TFM} file contains only residues of character codes modulo~256.
+This convention is intended for oriental languages, when there are many
+character shapes but few distinct widths.)
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+The character displacements (|dx,dy|) are the values of \MF's \&{chardx}
+and \&{chardy} parameters; they are in units of |scaled| pixels;
+i.e., |dx| is in horizontal pixel units times $2^{16}$, and |dy| is in
+vertical pixel units times $2^{16}$. This is the intended amount of
+displacement after typesetting the character; for \.{DVI} files, |dy|
+should be zero, but other document file formats allow nonzero vertical
+displacement.
+
+The character width~|w| duplicates the information in the \.{TFM} file; it
+is $2^{24}$ times the ratio of the true width to the font's design size.
+
+The backpointer |p| points to the character's |boc|, or to the first of
+a sequence of consecutive \\{xxx} or |yyy| or |no_op| commands that
+immediately precede the |boc|, if such commands exist; such ``special''
+commands essentially belong to the characters, while the special commands
+after the final character belong to the postamble (i.e., to the font
+as a whole). This convention about |p| applies also to the backpointers
+in |boc| commands, even though it wasn't explained in the description
+of~|boc|. @^backpointers@>
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+Pointer |p| might be |-1| if the character exists in the \.{TFM} file
+but not in the \.{GF} file. This unusual situation can arise in \MF\ output
+if the user had |proofing<0| when the character was being shipped out,
+but then made |proofing>=0| in order to get a \.{GF} file.
+
+These |p| pointers are not currently being used in this program, instead we
+store all rasters as received in the |mm_store| and index then by
+|glyph_ptr|. The role of a |-1| value for |p| is take over by a |-1| in
+the |glyph_ptr| array.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the character locators, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~131, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @'337 in octal). \MF\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{GF} file makes it feasible for
+\.{GF}-reading programs to find the postamble first, on most computers,
+even though \MF\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{GF} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{GF} reader can discover all the information needed for individual characters.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{GF}
+format has been designed to work most efficiently with modern operating systems.
+But if \.{GF} files have to be processed under the restrictions of standard
+\PASCAL, one can simply read them from front to back. This will
+be adequate for most applications. However, the postamble-first approach
+would facilitate a program that merges two \.{GF} files, replacing data
+from one that is overridden by corresponding data in the other.
+
+@* Reading the font information.
+\.{DVI} file format does not include information about character widths
+nor the detailed raster information. \.{DVIIMP} gets this information
+directly from the (\.{GF}) files.
+@.GF {\rm files}@>
+
+The task facing \.{DVIIMP} is quite different from that facing \.{DVItype}
+which has a comparatively easy task in this regard, since it needs only a
+few words of information from each font. We will follow this earlier
+program as much as possible in our use of file names and related details
+but our data structure will necessarily be somewhat more complicated.
+
+We follow \.{DVItype} to the extent of listing the current number of known
+fonts as |nf|. Each known font has an internal number |f|, where |0<=f<nf|;
+the external number of this font, i.e., its font identification number in
+the \.{DVI} file, is |font_num[f]|, and the external name of this font is
+the string that occupies positions |font_name[f]| through
+|font_name[f+1]-1| of the array |names|. The latter array consists of
+|ASCII_code| characters, and |font_name[nf]| is its first unoccupied
+position.
+
+Fonts containing more than 128 characters require special attention since
+\.{Imagen} will only accept 0 to 127 as valid character numbers. An easy
+way out of this difficulty is to assign |f| numbers starting at the
+largest font number (95) that \.{Imagen} will accept (and progressing
+downward) as |im_extension| family numbers that can be assigned to the
+over 127 characters of large fonts and that can be downloaded with |c-128|
+as the \.{Imagen} identification. A record of this relationship is
+maintained in an |im_extension[cur_font]| array.
+
+We will find it necessary, occasionally, to reuse the |mm_store| space
+and to make this possible we define a |free_limit| parameter.
+This parameter is set initially to |mm_max|. The following
+|make_space| procedure is used to free space.
+Note that this does not prevent the printing of those glyphs that have
+been downloaded but the raster data for those glyphs that have not been
+downloaded will have to be reread from the |gf| file should any of these
+be subsequently requested.
+
+@p procedure make_space;
+var i,j,k,q: integer;
+begin
+@!debug
+print(' overwriting font ');
+print_ln(font_order[0]:1,' ');
+gubed@/
+j:=data_start[font_order[0]];
+k:=data_start[font_order[1]];
+q:=glyph_ptr[k];
+if q>12 then free_limit:=q-1 else free_limit:=mm_size;
+for i:=j to k-1 do
+ if glyph_ptr[i]>=4 then glyph_ptr[i]:=0; {mark as no longer available}
+for i:=0 to max_fonts-1 do font_order[i]:=font_order[i+1];
+end;
+
+@ We also follow the \.{DVItype} example of storing the glyph widths
+(measured in \.{DVI} units) in a |width| array that is indexed by values
+stored in a |data_base| array. This |data_base| is in turn indexed by the
+internal font number and its values point to pseudo starting locations in
+the |width| array where the first glyph widths for the fonts would be
+stored were there a zero numbered glyph in the font. The actualy starting
+location for each font's data in the |width| table is displaced forward
+by |font_bc| where |font_bc| is the lowest character number that is
+contained in each particular font. The values in the |data_base| array
+are, of course, also used to access the |pixel_width| values (measured in
+pixels) since it will be organized in an identical way to that used with
+the |width| table.
+
+Gaining access to the font raster details, stored in |mm_store|, is a
+slightly longer process because the spaces occupied by the raster details
+will usually vary from glyph to glyph. We handle this matter by having
+yet another indexing stage where the starting location in |mm_store| for
+each individual glyph is stored in a |glyph_ptr| array that is accessed,
+in turn, by using the same |data_base| value that is used to locate the
+|width| and |pixel_width| values.
+
+Normally, this double-indexing recall needs be done but once for
+each used glyph since all glyphs are stored internally in the \.{IMAGEN}
+on the first occasions when they are used. As will be noted later, we
+signal the fact that any particular glyph has been down-loaded by
+negating its reference number in the |glyph_ptr| array.
+
+@d char_width_end(#)==#]
+@d char_width(#)==width[data_base[#]+char_width_end
+@d invalid_width==@'17777777777
+@d stow(#)==begin mm_store[m1,m2]:=#;
+ if (mm<free_limit) and ((mm+8)>free_limit) then make_space;
+ if m2<m2_max then begin incr(m2); incr(mm); end
+ else
+ begin
+ m2:=4; {|-4<m2<4| freed for down-loading and |make_space| signs}
+ mm:=mm+5;
+ if m1<m1_max then incr(m1) else begin m1:=0; mm:=4; end;
+ end;
+ end
+
+@<Glob...@>=
+@!font_num:array [0..max_fonts] of integer; {external font numbers}
+@!font_m_val:array [0..max_fonts] of integer; {overall font magnification}
+@!font_name:array [0..max_fonts] of 0..name_size; {starting positions
+ of external font names}
+@!names:array [0..name_size] of ASCII_code; {characters of names}
+@!font_check_sum:array [0..max_fonts] of integer; {check sums}
+@!font_scaled_size:array [0..max_fonts] of integer; {scale factors}
+@!font_design_size:array [0..max_fonts] of integer; {design sizes}
+@!font_space:array [0..max_fonts] of integer; {boundary between ``small''
+ and ``large'' spaces}
+@!font_bc:array [0..max_fonts] of integer; {beginning characters in fonts}
+@!font_ec:array [0..max_fonts] of integer; {ending characters in fonts}
+@!data_base:array [0..max_fonts] of integer; {index into font data tables}
+@!width:array [0..max_glyphs] of integer; {character widths, in \.{DVI} units}
+@!in_width:array[0..255] of integer; {\.{TFM} width data in \.{DVI} units}
+@!tfm_check_sum:integer; {check sum found in |tfm_file|}
+@!nf:0..max_fonts; {the number of known fonts}
+@!nf2: 0..95; {the lower limit of font extension numbers}
+@!im_extension: array[0..max_fonts] of integer; {relating extension numbers}
+@!width_ptr:0..max_glyphs; {the number of known character widths}
+@!bc,ec:integer; {beginning and ending c in current font}
+@!w_byte: array[0..max_char_no, 0..3] of eight_bits; {to hold |width| bytes}
+@!gf_ptr: array[0..max_char_no] of integer; {to hold valid glyph indicators}
+
+@ @<Set init...@>=
+nf:=0; width_ptr:=0; font_name[0]:=0; font_space[0]:=0;
+nf2:=95; {limit to usable font numbers set by Imagen}
+for i:=0 to max_fonts do im_extension[i]:=-1; {marked as not assigned}
+
+@ It is, of course, a simple matter to print the name of a given font.
+
+@p procedure print_font(@!f:integer); {|f| is an internal font number}
+var k:0..name_size; {index into |names|}
+begin if f=nf then print('UNDEFINED!')
+@.UNDEFINED@>
+else begin for k:=font_name[f] to font_name[f+1]-1 do
+ print(xchr[names[k]]);
+ end;
+end;
+
+@ The following procedure is used to print the font-name extension as
+used on the \.{SAIL} computer at Stanford. It condenses a possibly 4-digit
+number into three characters by using the letters A to Z for the first character
+for extensions in the range from 1000 to 3599 and simply reporting an extension
+of .GF for those unlikely cases where the value is 3600 or greater.
+@^system dependencies@>
+
+@p procedure print_extension(m:integer);
+begin
+print('.');
+if m < 3600 then
+ begin
+ if m < 1000 then print(xchr[(m div 100)+@'60])
+ else print(xchr[(m div 100)+@'67]);
+ print(xchr[(m mod 100) div 10+@'60]);
+ print(xchr[m mod 10+@'60]);
+ end
+else print('GF');
+end;
+
+@ The global variable |gf_check_sum| is set to the check sum that
+appears in the current \.{GF} file.
+
+@<Glob...@>=
+@!gf_check_sum:integer; {check sum found in |gf_file|}
+
+@ We will need a number of procedures to extract the necessary inforation
+fron a \.{GF} file, assuming that the file has just been successfully
+reset so that we are ready to read its first byte. Only a limited amount
+of validity checking of the \.{GF} file will be done since \.{GF} files
+are almost always valid, and since the \.{GFtype} utility program has been
+specifically designed to diagnose \.{GF} errors. The procedure simply
+returns |false| if it detects anything amiss in the \.{GF} data.
+
+Since we are going to defer the creation of an \.{imPRESS} |bgly| command
+for each glyph until the first time that it is actually called, we will
+now only decipher the |gf| commands far enough to determine if they are to
+be saved and to store them away in as compact a form as possible.
+
+As mentioned earlier, raster determining commands are stored in a large
+array, |mm_store|. This information is stored serially, as it is received,
+together with 8 bytes of preliminary information that must also be
+transmitted. The location of the first byte of information is recorded
+in the |glyph_ptr| array. To insure that this number will always be greater
+than 3 (since numbers in the range between -3 and +3 are used as special
+signals) we do not use the first 4 cells in |mm_store| (actually, the first
+4 cells in each of the four sections into which |mm_store| is divided).
+Later, when the glyph is first called for
+by the \.{DVI} file, we will generate an appropriate \.{IMAGEN} |bgly|
+command and complement the pointer value in the |glyph_ptr| array to show
+that this has been done. Finally, as will be explained in more detail
+later, we will have to arrange for the removal of the raster information
+for one or more fonts, to make space for other fonts. and we will have to
+store a record of this removal.
+
+We will find it convenient to define a |find_gf_postamble| function and a
+|read_gf_postamble| procedure. Since we will have occasion to deal with
+parameters associated with the GF commands, we will also define a function
+|first_gf_par| analogous to the |first_par| that we defined earlier.
+
+@p function find_gf_postamble:boolean;
+var q,@!k: integer;
+begin
+find_gf_postamble:=true;
+gf_post_loc:=gf_length-4;
+repeat if gf_post_loc=0 then find_gf_postamble:=false;
+move_to_gf_byte(gf_post_loc); k:=gf_byte; decr(gf_post_loc);
+until k<>223;
+if k<>gf_id_byte then find_gf_postamble:=false;
+move_to_gf_byte(gf_post_loc-3); q:=gf_signed_quad;
+if (q<0)or(q>gf_post_loc-3) then find_gf_postamble:=false;
+move_to_gf_byte(q); k:=gf_byte;
+if k<>post then find_gf_postamble:=false;
+@!debug
+print_ln( ' gf postamble at ',cur_gf_loc:1);
+gubed
+end;
+
+@ Having found the |gf_postamble|, we must now read it and stow the
+data away as as halfwords as required later by \.{IMAGEN}.
+
+@p procedure read_gf_postamble;
+var k,l:integer; {loop indices}
+@!p,q,@!m,@!c:integer; {general purpose registers}
+begin gf_post_loc:=cur_gf_loc-1;
+@.gf_postamble starts at byte n@>
+p:=gf_signed_quad;
+design_size:=gf_signed_quad; check_sum:=gf_signed_quad;@/
+hppp:=gf_signed_quad; vppp:=gf_signed_quad;@/
+magnification:=hppp/(65536.0*resolution/72.27);
+@<Report font specification disagreements@>;
+min_m:=gf_signed_quad; max_m:=gf_signed_quad;
+min_n:=gf_signed_quad; max_n:=gf_signed_quad;@/
+bc:=max_char_no; ec:=0;
+ {prepare for a determination in Process the character loc}
+@<Clear |w_byte| array@>;
+@<Process the character locations in the postamble@>;
+while not eof(gf_file) do m:=gf_byte; {to close out file}
+end;
+@#
+function first_gf_par(o:eight_bits):integer;
+begin case o of
+sixty_four_cases(paint_0): first_gf_par:=o-paint_0;
+paint1,skip1,char_loc,char_loc+1,xxx1: first_gf_par:=gf_byte;
+paint2,skip2,xxx1+1: first_gf_par:=gf_two_bytes;
+paint1+2,skip1+2,xxx1+2: first_gf_par:=gf_three_bytes;
+xxx1+3,yyy: first_gf_par:=gf_signed_quad;
+boc,boc1,eoc,skip0,no_op,pre,post,post_post,undefined_commands: first_gf_par:=0;
+one_sixty_five_cases(new_row_0): first_gf_par:=o-new_row_0;
+end;
+end;
+@#
+procedure copy_byte;
+var w:eight_bits;
+begin
+w:=gf_byte; stow(w);
+end;
+@#
+procedure stow_pair(@!w:integer);
+begin
+stow(w div @"100);
+stow(w mod @"100);
+end;
+@#
+procedure stow_signed_pair(@!w:integer);
+begin
+if w<0 then w:=w+@"10000;
+stow(w div @"100);
+stow(w mod @"100);
+end;
+
+@ @<Report font specification disagreements@>=
+if design_size<>font_design_size[cur_font]*16 then
+ begin print_nl; print('design sizes for font '); print_font(cur_font);
+ print_extension(font_m_val[cur_font]); print(' do not agree. ');
+ end;
+if (check_sum<>font_check_sum[cur_font]) and (check_sum<>0)
+ and (font_check_sum[cur_font]<>0) then
+ begin print_nl; print('check sums for font '); print_font(cur_font);
+ print_extension(font_m_val[cur_font]); print(' do not agree. ');
+ end;
+q:=round((resolution*65536/72.27)*(mag/1000.0)*
+ font_scaled_size[cur_font]/font_design_size[cur_font]);
+if ((q-(q div 100))>hppp) or ((q+(q div 100))<hppp) then
+ begin print_nl; print('at size values for font '); print_font(cur_font);
+ print_extension(font_m_val[cur_font]);
+ print(' disagree by more than one percent. ');
+ end;
+
+@ @<Clear |w_byte| array@>=
+for k:=0 to max_char_no do
+ begin
+ for l:=0 to 3 do w_byte[k,l]:=0;
+ gf_ptr[k]:=0; {so data of missing glyphs will be made available}
+ end;
+
+@ @<Process the character locations in the postamble@>=
+repeat k:=gf_byte;
+if (k=char_loc) or (k=char_loc+1) then
+ begin
+ c:=gf_byte;
+ if c>max_char_no then abort('Character number too large');
+ if c<bc then bc:=c; if c>ec then ec:=c;
+ if k=char_loc then
+ begin dx[c]:=gf_signed_quad div 65536; dy:=gf_signed_quad;
+ end
+ else begin dx[c]:=gf_byte; dy:=0;
+ end;
+@!debug
+ print(' k=',k:1,' c=',c:1,' dx=',dx[c]:1);
+gubed@/
+ w_byte[c,0]:=gf_byte;
+ w_byte[c,1]:=gf_byte;
+ w_byte[c,2]:=gf_byte;
+ w_byte[c,3]:=gf_byte;
+ gf_ptr[c]:=gf_signed_quad; {the |>0| values will mark existing glyphs}
+@!debug
+ print_ln(' k=',k:1,' gfptr=',gf_ptr[c]:1);
+gubed@/
+ k:=no_op;
+ end;
+until k<>no_op;
+
+@ Here is the main information we glean from the postamble together with
+some auxiliary parameters.
+
+@<Glob...@>=
+@!design_size: integer;
+@!hppp, @!vppp: integer;
+@!check_sum: integer;
+@!gf_post_loc: integer;
+@!magnification: real;
+@!dx: array [0..max_char_no] of integer;
+@!dy: integer; {not used since value should always be zero}
+@!total_glyphs:integer; {the total number of glyphs stored in |mm_store|}
+@!mm_store:packed array [0..m1_max,4..m2_max] of eight_bits;
+ {to store glyph information}
+@!mm,@!m1,@!m2:integer; {indices for |mm_store|}
+@!free_limit:integer; {|mm| value of last free location in |mm_store|}
+@!data_start:array [0..max_fonts] of integer; {|data_base+bc| for fonts}
+@!font_order:array [0..max_fonts] of integer; {font numbers in loaded order}
+@!gf_prev_ptr: integer; {location of next character}
+@!char_code: integer; {current character number}
+@!glyph_ptr: array[0..max_glyphs] of integer;
+ {pointers to |mm_store|}
+@!max_m,@!min_m,@!max_n,@!min_n: integer; {raster bounding parameters}
+@!row_count: integer; {used to correct the raster height figure}
+@!column_count:integer; {used to accumulate column counts}
+@!max_column_count:integer; {used to correct the raster width figure}
+
+@ @<Set init...@>=
+for i:=0 to max_glyphs do glyph_ptr[i]:=-1;
+ {mark glyphs as not being in the file}
+total_glyphs:=0;
+mm:=4; m1:=0; m2:=4; {|-4<mm<4| saved for signalling purposes}
+free_limit:=mm_max;
+for i:=0 to max_fonts do font_order[i]:=-1;
+
+@ A temporary procedure.
+
+@p
+@!debug
+procedure tabulate;
+var i,j,k,l:integer;
+begin
+print_nl;
+print_ln(' Contents of the glyph ptr table');
+print(' ');
+for j:=0 to 9 do print(j:7);
+print_nl;
+for i:=0 to 29 do
+ begin
+ print(i*10:3,' ');
+ for j:=0 to 9 do
+ begin
+ k:=glyph_ptr[10*i+j];
+ l:=k div m2_size;
+ k:=k mod m2_size;
+ print(l:1,',',k:1);
+ end;
+ print_nl;
+ end;
+end;
+gubed
+
+@ Here is the long awaited |in_gf| routine.
+
+@p function in_gf(@!z:integer):boolean; {input \.{GF} data or return |false|}
+label done,restart,
+ 9997, {go here when the format is bad}
+ 9998, {go here when the information cannot be loaded}
+ 9999; {go here to exit}
+var k:integer; {index for loops}
+@!nw:integer; {number of words in the width table}
+@!wp:0..max_glyphs; {new value of |width_ptr| after successful input}
+@!alpha,@!beta:integer; {quantities used in the scaling computation}
+@!c: integer; { used it index character number}
+@!o:integer; {used to hold |gf| commands}
+@!p:integer; {used to hold |gf| parameter}
+@!del_m:integer; {used to hold |gf| parameter}
+@!del_n:integer; {used to hold |gf| parameter}
+@!mm_save,@!m1_save,@!m2_save:integer; {temp save to allow for corrections}
+begin
+if not find_gf_postamble then
+ begin print_ln(' Trouble with postamble');
+ goto 9997;
+ end;
+read_gf_postamble;
+@<Check |width| table and |goto 9997| if there is a problem@>;
+@<Convert and store the width values@>;
+@<Process the gf preamble@>;
+@<Stow all of the glyph-raster info@>;
+@!debug
+tabulate; {Used to show the start of the |glyph_ptr| array}
+print_ln(' glyph-raster done');
+gubed@/
+width_ptr:=wp;
+in_gf:=true; goto 9999;
+9997: print_ln('---not loaded, GF file is bad');
+@.GF file is bad@>
+9998: in_gf:=false;
+9999: end;
+
+@ @<Check |width| table and...@>=
+font_bc[cur_font]:=bc; font_ec[cur_font]:=ec;
+if font_ec[cur_font]<font_bc[cur_font] then
+ font_bc[cur_font]:=font_ec[cur_font]+1;
+if width_ptr+font_ec[cur_font]-font_bc[cur_font]+1>max_glyphs then
+ begin print_ln('---not loaded, DVIIMP needs larger width table');
+ goto 9998;
+ end;
+wp:=width_ptr+font_ec[cur_font]-font_bc[cur_font]+1;
+nw:=ec+1-bc;
+@!debug
+print_ln(' bc=',bc:1,' ec=',ec:1,' nw=',nw:1);
+gubed@/
+if (nw=0)or(nw>256) then goto 9997;
+
+@ @<Process the gf preamble@>=
+open_gf_file;
+o:=gf_byte; {fetch the first byte}
+if o<>pre then begin
+ print_ln(' GF file does not start with |pre|');
+ goto 9997;
+ end;
+o:=gf_byte; {fetch the identification byte}
+if o<>gf_id_byte then begin
+ print_ln(' id =',o:1,' should be ',gf_id_byte:1);
+ goto 9997;
+ end;
+o:=gf_byte; {fetch the length of the introductory comment}
+while o>0 do
+ begin decr(o); p:=gf_byte;
+ end;
+
+@ An important part of |in_gf| is the width computation, which
+involves multiplying the relative widths in the \.{GF} file by the
+scaling factor in the \.{DVI} file. This fixed-point multiplication
+must be done with precisely the same accuracy by all \.{DVI}-reading programs,
+in order to validate the assumptions made by \.{DVI}-writing programs
+like \TeX82.
+
+Let us therefore summarize what needs to be done. Each width in a \.{GF}
+file appears as a four-byte quantity called a |fix_word|. A |fix_word|
+whose respective bytes are $(a,b,c,d)$ represents the number
+$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
+-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
+(No other choices of $a$ are allowed, since the magnitude of a \.{GF}
+dimension must be less than 16.) We want to multiply this quantity by the
+integer~|z|, which is known to be less than $2^{27}$. Let $\alpha=16z$.
+If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
+$d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
+16, to obtain a multiplier less than $2^{23}$, and we can compensate for
+this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
+$\beta=2^{4-e}$; we shall compute
+$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
+or the same quantity minus $\alpha$ if $a=255$. This calculation must be
+done exactly, for the reasons stated above; the following program does the
+job in a system-independent way, assuming that arithmetic is exact on
+numbers less than $2^{31}$ in magnitude.
+
+Whereas \.{DVItype} obtained the |pixel_width|s by rounding the |width|
+value, we obtain these values from the |dx| parameter associated with the
+|char_loc| command. It should be noted that |width[k]| is the
+device-independent width of some character in \.{DVI} units while
+|pixel_width[k]| is the corresponding pixel width of that character in an
+actual font.
+
+The macro |char_pixel_width| is set up to be analogous to |char_width|.
+
+@d char_pixel_width(#)==pixel_width[data_base[#]+char_width_end
+
+@d pixel_round(#)==round(conv*(#))
+
+@<Glob...@>=
+@!pixel_width:array[0..max_glyphs] of integer; {actual character widths,
+ in pixels}
+@!conv:real; {converts \.{DVI} units to pixels}
+@!true_conv:real; {converts unmagnified \.{DVI} units to pixels}
+@!numerator,@!denominator:integer; {stated conversion ratio}
+@!mag:integer; {magnification factor times 1000}
+@!empty_glyph: boolean; {foxing Imagen into accepting an empty glyph}
+
+@ @<Convert and store the width values@>=
+@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
+data_base[cur_font]:=width_ptr-bc;
+data_start[cur_font]:=width_ptr;
+wp:=width_ptr+ec-bc+1;
+c:=bc;
+for k:=width_ptr to wp-1 do begin
+ if gf_ptr[c]=0 then begin
+ width[k]:=invalid_width; pixel_width[k]:=0;
+@!debug
+ print(' invalid width for c=',c:1);
+gubed
+ end
+ else begin
+ width[k]:=(((((w_byte[c,3]*z)div@'400)
+ +(w_byte[c,2]*z))div@'400)+(w_byte[c,1]*z))div beta;
+ if (w_byte[c,0]>0) then
+ if (w_byte[c,0]<255) then begin
+ print_ln(' w byte=',w_byte[c,0]:1);
+ goto 9997
+ end
+ else width[k]:=width[k]-alpha;
+ pixel_width[k]:=dx[c];
+ end;
+@!debug
+ print(' dx=',dx[c]:1,' for ',c:1);
+ print(' [ ',c:1,']');
+gubed@/
+ incr(c);
+ end;
+
+@ @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>=
+begin alpha:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; alpha:=alpha+alpha;
+ end;
+beta:=256 div alpha; alpha:=alpha*z;
+end
+
+
+@ In those few cases (we hope) where a \.{GF} file is not available we
+will want to refer to the \.{TFM} file and leave space in the document for
+the missing glyphs. The following procedure is used for this purpose.
+
+@p function in_tfm(@!z:integer):boolean; {input \.{TFM} data or return |false|}
+label 9997, {go here when the format is bad}
+ 9998, {go here when the information cannot be loaded}
+ 9999; {go here to exit}
+var k:integer; {index for loops}
+@!lh:integer; {length of the header data, in four-byte words}
+@!nw:integer; {number of words in the width table}
+@!wp:0..max_glyphs; {new value of |width_ptr| after successful input}
+@!alpha,@!beta:integer; {quantities used in the scaling computation}
+begin @<Read past the header data; |goto 9997| if there is a problem@>;
+@<Store character-width indices at the end of the |width| table@>;
+@<Read and convert the width values, setting up the |in_width| table@>;
+@<Move the widths from |in_width| to |width|,
+ and append |pixel_width| values@>;
+width_ptr:=wp; in_tfm:=true; goto 9999;
+9997: print_ln('---not loaded, TFM file is bad');
+@.TFM file is bad@>
+9998: in_tfm:=false;
+9999: end;
+
+@ @<Read past the header...@>=
+read_tfm_word; lh:=b2*256+b3;
+read_tfm_word; font_bc[cur_font]:=b0*256+b1;
+font_ec[cur_font]:=b2*256+b3;
+if font_ec[cur_font]<font_bc[cur_font] then
+ font_bc[cur_font]:=font_ec[cur_font]+1;
+if width_ptr+font_ec[cur_font]-font_bc[cur_font]+1>max_glyphs then
+ begin print_ln('---not loaded, DVItype needs larger width table');
+@.DVItype needs larger...@>
+ goto 9998;
+ end;
+wp:=width_ptr+font_ec[cur_font]-font_bc[cur_font]+1;
+read_tfm_word; nw:=b0*256+b1;
+if (nw=0)or(nw>256) then goto 9997;
+for k:=1 to 3+lh do
+ begin if eof(tfm_file) then goto 9997;
+ read_tfm_word;
+ if k=4 then
+ if b0<128 then tfm_check_sum:=((b0*256+b1)*256+b2)*256+b3
+ else tfm_check_sum:=(((b0-256)*256+b1)*256+b2)*256+b3;
+ end;
+
+@ @<Store character-width indices...@>=
+if wp>0 then for k:=width_ptr to wp-1 do
+ begin read_tfm_word;
+ if b0>nw then goto 9997;
+ width[k]:=b0;
+ end;
+
+@ @<Read and convert the width values...@>=
+@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
+for k:=0 to nw-1 do
+ begin read_tfm_word;
+ in_width[k]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
+ if b0>0 then if b0<255 then goto 9997
+ else in_width[k]:=in_width[k]-alpha;
+ end
+
+@ @<Move the widths from |in_width| to |width|,
+ and append |pixel_width| values@>=
+if in_width[0]<>0 then goto 9997; {the first width should be zero}
+data_base[cur_font]:=width_ptr-font_bc[cur_font];
+if wp>0 then for k:=width_ptr to wp-1 do
+ if width[k]=0 then
+ begin width[k]:=invalid_width; pixel_width[k]:=0;
+ end
+ else begin width[k]:=in_width[width[k]];
+ pixel_width[k]:=pixel_round(width[k]);
+ glyph_ptr[k]:=-1;
+ end
+
+@ @<Stow all...@>=
+@!debug
+print_ln(' loading font ',cur_font:1,'[',m1:1,',',m2:1,'] ');
+print((free_limit div m2_size):1,'-',(free_limit mod m2_size):1,' ');
+gubed@/
+k:=0; while font_order[k]>=0 do incr(k);
+font_order[k]:=cur_font; {add this font to ordered list}
+repeat gf_prev_ptr:=cur_gf_loc;
+@<Pass |no_op|, |xxx| and |yyy| commands@>;
+if (o=boc) or (o=boc1) then
+ begin
+ m1_save:=m1; m2_save:=m2; {for width and height corrections}
+ mm_save:=m1*m2_size+m2; {to be stored in |glyph_ptr| when |c| is known}
+ if o=boc then begin @<Stow the |boc| information@> end
+ else begin @<Stow the |boc1| information@>;
+ end;
+glyph_ptr[data_base[cur_font]+c]:=mm_save; {save glyph start address}
+@!debug
+print(' (',cur_font:1,')',c:1,'[',m1:1,',',m2:1,']');
+gubed@/
+if empty_glyph then
+ begin
+ glyph_ptr[data_base[cur_font]+c]:=-1;
+ empty_glyph:=false;
+ end;
+ @<Stow the glyph details@>;
+ end;
+until o=post;
+
+@ As noted earlier, the parameters associated with the |boc| command are
+received from the |gf| file as |signed_quad|s and are converted into the
+form needed by the \.{IMAGEN} and then stowed into |mm_store| as
+|signed_pairs|, in keeping with the restricted range of value that the
+\.{IMAGEN} allows.
+
+@ @<Stow the |boc| information@>=
+incr(total_glyphs);
+char_code:=gf_signed_quad;
+p:=gf_signed_quad;
+c:=char_code mod 256;
+if c<0 then c:=c+256;
+if c>127 then if im_extension[cur_font]=-1 then
+ begin
+ if nf2=nf then
+ begin
+ print_ln(' ---Out of font storage space');
+ goto 9998;
+ end;
+ im_extension[cur_font]:=nf2; decr(nf2);
+ end;
+@!debug
+print(' boc[',c:1,']');
+if char_code<>c then
+ print(' in family ',(char_code-c) div 256 : 1);
+gubed@/
+min_m:=gf_signed_quad; max_m:=gf_signed_quad;
+min_n:=gf_signed_quad; max_n:=gf_signed_quad;
+if max_m-min_m<=0 then empty_glyph:=true else empty_glyph:=false;
+stow_signed_pair(max_m-min_m+1); {width}
+stow_signed_pair(-min_m); {left offset}
+stow_signed_pair(max_n-min_n+1); {height}
+stow_signed_pair(max_n); {top offset}
+
+@ Similarly, the one byte parameters associated with the
+|boc1| command are converted into the required form and stored into
+|mm_store| as |signed_pairs|.
+
+@ @<Stow the |boc1| information@>=
+incr(total_glyphs);
+char_code:=gf_byte;
+p:=-1;
+c:=char_code;
+if c>127 then if im_extension[cur_font]=-1 then
+ begin
+ if nf2=nf then
+ begin
+ print_ln(' ---Out of font storage space');
+ goto 9998;
+ end;
+ im_extension[cur_font]:=nf2; decr(nf2);
+ end;
+@!debug
+print_nl; print_nl;
+print(' boc1[',c:1,']');
+gubed@/
+del_m:=gf_byte; max_m:=gf_byte;
+del_n:=gf_byte; max_n:=gf_byte;
+if del_m<=0 then empty_glyph:=true else empty_glyph:=false;
+stow_signed_pair(del_m+1);
+stow_signed_pair(del_m-max_m);
+stow_signed_pair(del_n+1);
+stow_signed_pair(max_n);
+@!debug
+print_ln(' c=',c:1,' del_m+1=',del_m+1:1,' del_m-max_m=',del_m-max_m:1,
+' del_n+1=',del_n+1:1,' max_n=',max_n:1);
+gubed@/
+
+@ Having deciphered a |boc| command or a |boc1| command and having stored
+the necessary information that precedes the mask information in a |bgly|
+command, we can limit the variety of commands that are to be stored to
+only those commands actually needed to specify the mask portion of a
+|bgly| command.
+
+@ @<Pass |no_op|, |xxx| and |yyy| commands@>=
+repeat
+ o:=gf_byte;
+ if (o=yyy) then begin
+ p:=first_gf_par(o); o:=no_op;
+ end
+ else if (o>=xxx1) and (o<=xxx1+3) then begin
+ p:=first_gf_par(o);
+ while p>0 do begin q:=gf_byte; decr(p); end;
+ o:=no_op;
+ end;
+until o<>no_op;
+
+@ @<Stow the glyph details@>=
+max_column_count:=0; {set for the glyph}
+column_count:=0;
+row_count:=0;
+while true do begin
+ restart:
+ o:=gf_byte;
+ case o of
+ sixty_four_cases(paint_0): begin
+ column_count:=column_count+o-paint_0;
+{|print_ln(' s0 ',o:1);|}
+ end;
+ paint1: begin
+ stow(o); o:=gf_byte; column_count:=column_count+o;
+{|print_ln(' s1 ',o:1);|}
+ end;
+ paint2: begin
+ stow(o); o:=gf_byte;
+ stow(o); column_count:=column_count+256*o;
+ o:=gf_byte; column_count:=column_count+o;
+ end;
+ skip0: begin
+ incr(row_count);
+ if column_count>max_column_count then
+ max_column_count:=column_count;
+ column_count:=0;
+ end;
+ skip1: begin
+ stow(o); o:=gf_byte;
+ row_count:=row_count+1+o;
+ if column_count>max_column_count then
+ max_column_count:=column_count;
+ column_count:=0;
+ end;
+ one_sixty_five_cases(new_row_0): begin
+ incr(row_count);
+ if column_count>max_column_count then
+ max_column_count:=column_count;
+ column_count:=o-new_row_0;
+ end;
+ xxx1: begin
+ o:=gf_byte;
+ while o>0 do begin q:=gf_byte; decr(o); end;
+ goto restart;
+ end;
+ yyy: begin
+ o:=5;
+ while o>0 do begin q:=gf_byte; decr(o); end;
+ goto restart;
+ end;
+ no_op: goto restart;
+ eoc: goto done;
+ othercases
+ print_ln('! Unexpected command: ',o:1)
+ endcases;
+ stow(o);
+ end;
+done:
+stow(o); {this should be an |eoc| command}
+{|print_ln('S EOC');|}
+if column_count>0 then incr(row_count); {last row isn't terminated}
+if column_count>max_column_count then max_column_count:=column_count;
+mm_store[m1_save,m2_save]:=max_column_count div 256;
+if m2_save<m2_max then incr(m2_save) else
+ begin m2_save:=4; if m1_save<m1_max then incr(m1_save)
+ else m1_save:=0;
+ end;
+mm_store[m1_save,m2_save]:=max_column_count mod 256;
+if m2_save+3<m2_size then m2_save:=m2_save+3 else
+ begin m2_save:=m2_save+7-m2_size; if m1_save<m1_max then incr(m1_save)
+ else m1_save:=0;
+ end;
+mm_store[m1_save,m2_save]:=row_count div 256;
+if m2_save<m2_max then incr(m2_save) else
+ begin m2_save:=4; if m1_save<m1_max then incr(m1_save)
+ else m1_save:=0;
+ end;
+mm_store[m1_save,m2_save]:=row_count mod 256;
+
+@* Optional modes of output.
+As normally compiled, the |dialog| routine is not called and \.{DVIIMP}
+operated in the |errors_only| mode. One can remove the brackets ( {|...|} )
+that surround the |dialog| call in the main program module and
+\.{DVIIMP} will then print different quantities of information based on some
+options that the user must specify: The |out_mode| level is set to one of
+four values (|errors_only|, |terse|, |verbose|, |the_works|), giving
+different degrees of output; and the typeout can be confined to a
+restricted subset of the pages by specifying the desired starting page and
+the maximum number of pages. Furthermore there is an option to specify the
+resolution of an assumed discrete output device, so that pixel-oriented
+calculations will be shown; and there is an option to override the
+magnification factor that is stated in the \.{DVI} file.
+
+The starting page is specified by giving a sequence of 1 to 10 numbers or
+asterisks separated by dots. For example, the specification `\.{1.*.-5}'
+can be used to refer to a page output by \TeX\ when $\.{\\count0}=1$
+and $\.{\\count2}=-5$. (Recall that |bop| commands in a \.{DVI} file
+are followed by ten `count' values.) An asterisk matches any number,
+so the `\.*' in `\.{1.*.-5}' means that \.{\\count1} is ignored when
+specifying the first page. If several pages match the given specification,
+\.{DVIIMP} will begin with the earliest such page in the file. The
+default specification `\.*' (which matches all pages) therefore denotes
+the page at the beginning of the file.
+
+When the modified \.{DVIIMP} begins, it engages the user in a brief dialog
+so that the options will be specified. This part of \.{DVIIMP} requires
+nonstandard \PASCAL\ constructions to handle the online interaction; so it
+may not be easy to allow for this dialog,
+and if so, one should simply to stick to the
+default options (starting page `\.*' (but printed in
+reverse order),
+|max_pages=1000|, |resolution=300.0|, |new_mag=0|). On other hand, the
+system-dependent routines that are needed are not complicated, so it should
+not be terribly difficult to introduce them.
+@^system dependencies@>
+
+@<Glob...@>=
+@!max_pages:integer; {at most this many |bop..eop| pages will be printed}
+@!resolution:real; {pixels per inch}
+@!new_mag:integer; {if positive, overrides the postamble's magnification}
+
+@ The starting page specification is recorded in two global arrays called
+|start_count| and |start_there|. For example, `\.{1.*.-5}' is represented
+by |start_there[0]=true|, |start_count[0]=1|, |start_there[1]=false|,
+|start_there[2]=true|, |start_count[2]=-5|.
+We also set |start_vals=2|, to indicate that count 2 was the last one
+mentioned. The other values of |start_count| and |start_there| are not
+important, in this example.
+
+@<Glob...@>=
+@!start_count:array[0..9] of integer; {count values to select starting page}
+@!start_there:array[0..9] of boolean; {is the |start_count| value relevant?}
+@!start_vals:0..9; {the last count considered significant}
+@!count:array[0..9] of integer; {the count values on the current page}
+
+@ @<Set init...@>=
+max_pages:=1000; start_vals:=0; start_there[0]:=false; new_mag:=0;
+
+@ Here is a simple subroutine that tests if the current page might be the
+starting page.
+
+@p function start_match:boolean; {does |count| match the starting spec?}
+var k:0..9; {loop index}
+@!match:boolean; {does everything match so far?}
+begin match:=true;
+for k:=0 to start_vals do
+ if start_there[k]and(start_count[k]<>count[k]) then match:=false;
+start_match:=match;
+end;
+
+@ The |input_ln| routine waits for the user to type a line at his or her
+terminal; then it puts ASCII-code equivalents for the characters on that line
+into the |buffer| array. The |term_in| file is used for terminal input,
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!term_in:text_file; {the terminal, considered as an input file}
+@!term_out:text_file; {the terminal, considered as an output file}
+
+@ Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall invoke a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+
+@ During the dialog, \.{DVIIMP} will treat the first blank space in a
+line as the end of that line. Therefore |input_ln| makes sure that there
+is always at least one blank space in |buffer|.
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+var k:0..terminal_line_length;
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+k:=0;
+while (k<terminal_line_length)and not eoln(term_in) do
+ begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
+ end;
+buffer[k]:=" ";
+end;
+
+@ The global variable |buf_ptr| is used while scanning each line of input;
+it points to the first unread character in |buffer|.
+
+@<Glob...@>=
+@!buf_ptr:0..terminal_line_length; {the number of characters read}
+
+@ Here is a routine that scans a (possibly signed) integer and computes
+the decimal value. If no decimal integer starts at |buf_ptr|, the
+value 0 is returned. The integer should be less than $2^{31}$ in
+absolute value.
+
+@p function get_integer:integer;
+var x:integer; {accumulates the value}
+@!negative:boolean; {should the value be negated?}
+begin if buffer[buf_ptr]="-" then
+ begin negative:=true; incr(buf_ptr);
+ end
+else negative:=false;
+x:=0;
+while (buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9") do
+ begin x:=10*x+buffer[buf_ptr]-"0"; incr(buf_ptr);
+ end;
+if negative then get_integer:=-x @+ else get_integer:=x;
+end;
+
+@ The selected options are put into global variables by the |dialog|
+procedure, which is called just as \.{DVIIMP} begins.
+@^system dependencies@>
+
+@p procedure dialog;
+label 2,3,4,5;
+var k:integer; {loop variable}
+begin rewrite(term_out); {prepare the terminal for output}
+write_ln(term_out,banner);
+@<Determine the desired |start_count| values@>;
+@<Determine the desired |max_pages|@>;
+@<Determine the desired |resolution|@>;
+@<Determine the desired |new_mag|@>;
+@<Print all the selected options@>;
+end;
+
+@ @<Determine the desired |start...@>=
+2: write(term_out,'Starting page (default=*): ');
+start_vals:=0; start_there[0]:=false;
+input_ln; buf_ptr:=0; k:=0;
+if buffer[0]<>" " then
+ repeat if buffer[buf_ptr]="*" then
+ begin start_there[k]:=false; incr(buf_ptr);
+ end
+ else begin start_there[k]:=true; start_count[k]:=get_integer;
+ end;
+ if (k<9)and(buffer[buf_ptr]=".") then
+ begin incr(k); incr(buf_ptr);
+ end
+ else if buffer[buf_ptr]=" " then start_vals:=k
+ else begin write(term_out,'Type, e.g., 1.*.-5 to specify the ');
+ write_ln(term_out,'first page with \count0=1, \count2=-5.');
+ goto 2;
+ end;
+ until start_vals=k
+
+@ @<Determine the desired |max_pages|@>=
+3: write(term_out,'Maximum number of pages (default=1000000): ');
+max_pages:=1000000; input_ln; buf_ptr:=0;
+if buffer[0]<>" " then
+ begin max_pages:=get_integer;
+ if max_pages<=0 then
+ begin write_ln(term_out,'Please type a positive number.');
+ goto 3;
+ end;
+ end
+
+@ @<Determine the desired |resolution|@>=
+4: write(term_out,'Assumed device resolution');
+write(term_out,' in pixels per inch (default=300/1): ');
+resolution:=300.0; input_ln; buf_ptr:=0;
+if buffer[0]<>" " then
+ begin k:=get_integer;
+ if (k>0)and(buffer[buf_ptr]="/")and
+ (buffer[buf_ptr+1]>"0")and(buffer[buf_ptr+1]<="9") then
+ begin incr(buf_ptr); resolution:=k/get_integer;
+ end
+ else begin write(term_out,'Type a ratio of positive integers;');
+ write_ln(term_out,' (1 pixel per mm would be 254/10).');
+ goto 4;
+ end;
+ end
+
+@ @<Determine the desired |new_mag|@>=
+5: write(term_out,'New magnification (default=0 to keep the old one): ');
+new_mag:=0; input_ln; buf_ptr:=0;
+if buffer[0]<>" " then
+ if (buffer[0]>="0")and(buffer[0]<="9") then new_mag:=get_integer
+ else begin write(term_out,'Type a positive integer to override ');
+ write_ln(term_out,'the magnification in the DVI file.');
+ goto 5;
+ end
+
+@ After the dialog is over, we print the options so that the user
+can see what \.{DVIIMP} thought was specified.
+
+@<Print all the selected options@>=
+print_ln('Options selected:');
+@.Options selected@>
+print(' Starting page = ');
+for k:=0 to start_vals do
+ begin if start_there[k] then print(start_count[k]:1)
+ else print('*');
+ if k<start_vals then print('.')
+ else print_ln(' ');
+ end;
+print_ln(' Maximum number of pages = ',max_pages:1);
+print_ln(' Resolution = ',resolution:12:8,' pixels per inch');
+if new_mag>0 then print_ln(' New magnification factor = ',new_mag/1000:8:3)
+
+@* Identifying and loading fonts.
+\.{DVIIMP} stores the raster information relating to the glyphs that it
+uses in a large |mm_store| array and stores the location of these rasters
+and information relating to their state in a |glyph_ptr| array. Additional
+|width| and |pixel_width| information is stored in still other arrays.
+
+It is usually not possible to provide a large enough |mm_store| space
+for all of the fonts that may be used in some documents. \.{DVIIMP}
+provides the facility for removing fonts from |mm_store| to make space for
+additional fonts and then for restoring the removed fonts if this becomes
+necessary.
+
+The general procedure is to
+read the \.{DVI} postamble first to get the desired |fnt_def1|
+information and to store this identifying information initially without
+storing the font rasters. An array |font_state[f]| is used to keep a
+record of the state of all fonts with the values set to 0 when the font
+identifying information is read. Later, when a |fnt_num| command is
+encountered in the body of the \.{DVI} file, the rasters for the entire
+font are read in and the |font_state| value for this font is changed to 1.
+However, glyphs are only downloaded as they are needed for the first time.
+
+The location and state for each individual glyph in all the fonts used is
+kept in the |glyph_ptr| array. This array is initially set to -1,
+indicating that the referenced glyphs either do not exist or that they
+have not yet been read into the |mm_store| memory. The individual glyph
+pointers are then set to positive values (actually, greater than 3) when
+the font rasters are read in, recording the position in the |mm_store|
+where the glyph is stored. These numbers are negated when each individual
+glyph is downloaded. Finally, if it becomes necessary to remove rasters
+to make space for other fonts, the positive |glyph_ptr| values for all
+glyphs of the removed fonts are set to zero without touching the negative
+pointer values (which still indicate the downloaded or non-existant states
+of the glyphs in question).
+
+Removing the rasters for the downloaded glyphs does not in any way prevent the
+continued use of these particular glyphs and no effort is made to reload any
+particular font until a request is encountered for a removed non-down-loaded
+glyph, as signalled by encountering a 0 value in the |glyph_ptr| array. At
+this time, only the non-down-loaded glyphs of the reloaded font are restored,
+with a possible substantial reduction in the space requirements as compared
+with the font's initial needs, since most of the more commonly used
+glyphs may have already been downloaded.
+
+A number of different utility procedures and functions will be needed.
+
+@<Glob...@>=
+@!font_state:array[0..max_fonts] of integer; {0 unloaded, 1 loaded}
+@!font_a_val:array[0..max_fonts] of integer; {length of directory name}
+@!font_l_val:array[0..max_fonts] of integer; {length of font name}
+@!scale_val:array [0..12] of integer; {table of preferred font scale values}
+
+@ @<Set init...@>=
+scale_val[0]:=round(1.0954*resolution);
+jj:=1.0;
+for i:=1 to 7 do
+ begin jj:=1.2*jj; scale_val[i]:=round(jj*resolution);
+@!debug
+ print_ln(' i=',i:1,' jj=',jj:1,' scale val=',scale_val[i]:1);
+gubed
+ end;
+scale_val[8]:=4*round(resolution);
+ {magnifications of 4000 and 5000 are sometines used}
+scale_val[9]:=5*round(resolution);
+scale_val[10]:=6*round(resolution);
+scale_val[11]:=7*round(resolution);
+scale_val[12]:=8*round(resolution);
+
+@ A minor problem in specifying the sizes of scaled fonts arises because
+of the fact that \.{\\magstep} definitions are in terms of the rounded
+values based on the magnification times 1000. For example, one will get
+different values for 1)~a magnification of 1200 as applied to a font
+scaled \.{\\magstep4}, and for 2)~a magnification of 1000 as applied to a
+font scaled \.{\\magstep5}. The following table and function provides the
+mechanism for resolving these differences by identifying the nearest match
+in terms of the overall actual magnification times the resolution. At
+\.{SAIL}, this figure is used as the file-name extension for standard
+\.{GF} files. @^system dependencies@>
+
+@p function reconcile_scale(m:integer):integer;
+label done;
+var i:0..12;
+begin
+reconcile_scale:=m;
+for i:=0 to 12 do
+ if abs(m-scale_val[i]) < abs(m-scale_val[i+1]) then
+ begin
+ if abs(m-scale_val[i])<4 then reconcile_scale:=scale_val[i];
+ goto done;
+ end;
+done: end;
+
+@ The following subroutine does the necessary things when a \\{fnt\_def}
+command is being processed in the postamble.
+
+@p procedure identify_font(@!e:integer); {|e| is an external font number}
+var f:0..max_fonts;
+@!p:integer; {length of the area/directory spec}
+@!n:integer; {length of the font name proper}
+@!c,@!q,@!d:integer; {check sum, scaled size, and design size}
+@!k:0..name_size; {indices into |names|}
+@!m: integer; {available for use in |mag| effect caculations}
+begin if nf=max_fonts then abort('DVIIMP capacity exceeded (max fonts=',
+ max_fonts:1,')!');
+@.DVIIMP capacity exceeded...@>
+font_num[nf]:=e; f:=0;
+while font_num[f]<>e do incr(f);
+@<Read the font parameters into position for font |nf|@>;
+@<Verify |font_scaled_size| and |font_design_size| for size@>;
+font_state[nf]:=0; {font identified but not read in}
+font_space[nf]:=q div 6; {this is a 3-unit ``thin space''}
+incr(nf); {signalling completion of identification}
+font_space[nf]:=0; {for |out_space| and |out_vmove|}
+end;
+
+@ @<Read the font parameters into position for font |nf|...@>=
+c:=signed_quad; font_check_sum[nf]:=c;@/
+q:=signed_quad; font_scaled_size[nf]:=q;@/
+d:=signed_quad; font_design_size[nf]:=d;@/
+p:=get_byte; font_a_val[nf]:=p;@/
+n:=get_byte; font_l_val[nf]:=n;@/
+if font_name[nf]+n+p>name_size then
+ abort('DVIIMP capacity exceeded (name size=',name_size:1,')!');
+@.DVIIMP capacity exceeded...@>
+font_name[nf+1]:=font_name[nf]+n+p;
+if n+p=0 then abort(' null n+p ')
+@.null n+p@>
+else for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=get_byte;
+m:=round((0.3*mag*q)/d);
+if (m>=round(1.05*resolution)) and (m<1500) then m:=reconcile_scale(m);
+font_m_val[nf]:=m;
+@!debug
+incr(nf);
+print_font(nf-1);
+print('.',m:1,' ');
+print_ln(' e=',e:1,' f=',nf:1,' c=',c:1,' q=',q:1,' d=',d:1,
+ ' p=',p:1,' n=',n:1);
+decr(nf);
+gubed
+
+@ @<Verify |font_scaled_size| and |font_design_size| for size@>=
+if (q<=0)or(q>=@'1000000000) then
+ print('---may not load, bad scale (',q:1,')!')
+@.bad scale@>
+ else if (d<=0)or(d>=@'1000000000) then
+ print('---may not load, bad design size (',d:1,')!');
+@.bad design size@>
+
+@ It will be desirable to skip over the |fnt_def1| commands that are found in
+the body of the \.{DVI} file as our method of reading the pages in reverse
+order makes it impractical for us to use them.
+
+@p procedure skip_it; {to bypass the |fnt_def1| commands in the body}
+var i,j,k: integer;
+begin
+for i:=1 to 13 do j:=get_byte;
+j:=j+get_byte;
+if j>0 then for i:=1 to j do k:=get_byte;
+end;
+
+@ We will have occasion to call the following from two different locations.
+
+@p procedure get_gf_file;
+var
+@!p:integer; {length of the area/directory spec}
+@!n:integer; {length of the font name proper}
+@!r:0..name_length; {index into |cur_name|}
+@!k:0..name_size; {indices into |names|}
+@!m: integer; {available for use in |mag| effect caculations}
+begin
+m:=font_m_val[cur_font];
+p:=font_a_val[cur_font];
+n:=font_l_val[cur_font];
+@<Move font name into the |cur_name| string@>;
+@!debug
+print_font(cur_font); print('.',m:1);
+print('(',cur_font:1,') ');
+gubed@/
+open_gf_file;
+
+if eof(gf_file) then
+ begin
+ print_nl;
+ print_font(cur_font); print_extension(m);
+@!debug
+ print('(',cur_font:1,') ');
+gubed@/
+ print(' not found');
+ @<Move font name into the |cur_tfm_name| string@>;
+ open_tfm_file;
+ if eof(tfm_file) then
+ begin
+ print(' and there is no |tfm| file ');
+ font_state[cur_font]:=-2;
+ end
+ else
+ begin
+ print(', characters will be left blank.');
+ font_state[cur_font]:=2;
+ end;
+ end;
+end;
+
+@ If |p=0|, i.e., if no font directory has been specified, \.{DVIIMP}
+is supposed to use the default font directory, which is a
+system-dependent place where the standard fonts are kept.
+The string variable |default_directory| contains the name of this area.
+@^system dependencies@>
+
+@d default_directory_name=='TeXGFs:' {change this to the correct name}
+@d default_directory_name_length=7 {change this to the correct length}
+@d dflt_tfm_directory_name=='TeXfonts:' {change this to the correct name}
+@d dflt_tfm_directory_name_length=9 {change this to the correct length}
+
+@<Glob...@>=
+@!default_directory:packed array[1..default_directory_name_length] of char;
+@!dflt_tfm_directory:packed array[1..dflt_tfm_directory_name_length] of char;
+
+@ @<Set init...@>=
+default_directory:=default_directory_name;
+dflt_tfm_directory:=dflt_tfm_directory_name;
+
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{GF} file for the current font. This usually means that we need to
+prepend the name of the default directory, and
+to append the suffix `\.{.GF}'. Furthermore, we change lower case letters
+to upper case, since |cur_name| is a \PASCAL\ string.
+@^system dependencies@>
+
+@<Move font name into the |cur_name| string@>=
+for k:=1 to name_length do cur_name[k]:=' ';
+if p=0 then
+ begin for k:=1 to default_directory_name_length do
+ cur_name[k]:=default_directory[k];
+ r:=default_directory_name_length;
+ end
+else r:=0;
+for k:=font_name[cur_font] to font_name[cur_font+1]-1 do
+ begin incr(r);
+ if r+4>name_length then
+ abort('DVIIMP capacity exceeded (max font name length=',
+ name_length:1,')!');
+@.DVIIMP capacity exceeded...@>
+ if (names[k]>="a")and(names[k]<="z") then
+ cur_name[r]:=xchr[names[k]-@'40]
+ else cur_name[r]:=xchr[names[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='G'; cur_name[r+3]:='F';
+{|cur_name[r+4]:='M';|}
+
+@ Normally, we only need to reference the \.{GF} files. On those
+occasions when no \.{GF} file is to be found we will want to obtain the
+glyph widths from a \.{TFM} file.
+The following module takes care of setting the external name of this
+\.{TFM} file.
+
+@<Move font name into the |cur_tfm_name| string@>=
+for k:=1 to name_length do cur_tfm_name[k]:=' ';
+if p=0 then
+ begin for k:=1 to dflt_tfm_directory_name_length do
+ cur_tfm_name[k]:=dflt_tfm_directory[k];
+ r:=dflt_tfm_directory_name_length;
+ end
+else r:=0;
+for k:=font_name[cur_font] to font_name[cur_font+1]-1 do
+ begin incr(r);
+ if r+4>name_length then
+ abort('DVIIMP capacity exceeded (max font name length=',
+ name_length:1,')!');
+@.DVIIMP capacity exceeded...@>
+ if (names[k]>="a")and(names[k]<="z") then
+ cur_tfm_name[r]:=xchr[names[k]-@'40]
+ else cur_tfm_name[r]:=xchr[names[k]];
+ end;
+cur_tfm_name[r+1]:='.'; cur_tfm_name[r+2]:='T';
+cur_tfm_name[r+3]:='F'; cur_tfm_name[r+4]:='M';
+
+@ We now come to the routines for reloading a font that has been removed.
+
+@p procedure reload_font;
+label done, restart;
+var k:integer; {index for loops}
+@!c: integer; { used it index character number}
+@!o:integer; {used to hold |gf| commands}
+@!p:integer; {used to hold |gf| parameter}
+@!a:integer; {used to hold |gf| parameter}
+@!del_m:integer; {used to hold |gf| parameter}
+@!del_n:integer; {used to hold |gf| parameter}
+@!mm_save,@!m1_save,@!m2_save:integer; {to allow corrections}
+begin
+get_gf_file;
+@<Skip over the preamble@>;
+@<Restow glyph rasters that have not been downloaded@>;
+font_state[cur_font]:=1; {signalling that font is loaded}
+end;
+
+@ @<Skip over the preamble@>=
+o:=gf_byte; {fetch the first byte}
+o:=gf_byte; {fetch the identification byte}
+o:=gf_byte; {fetch the length of the introductory comment}
+while o>0 do
+ begin decr(o); p:=gf_byte;
+ end;
+
+@ @<Restow glyph rasters that have not been downloaded@>=
+k:=0; while font_order[k]>=0 do incr(k);
+font_order[k]:=cur_font; {add this font to ordered list}
+repeat gf_prev_ptr:=cur_gf_loc;
+@<Pass |no_op|, |xxx| and |yyy| commands@>;
+if (o=boc) or (o=boc1) then begin
+ if o=boc then @<Read the |boc| information@>
+ else @<Read the |boc1| information@>;
+@!debug
+ print(' c=',c:1);
+gubed
+ if glyph_ptr[data_base[cur_font]+c]<0 then
+ @<Pass over the raster details@> {glyph has been downloaded}
+ else begin
+ mm_save:=mm; m1_save:=m1; m2_save:=m2;
+ {for possible width and height corrections}
+ glyph_ptr[data_base[cur_font]+c]:=m1*m2_size+m2;
+ {save glyph starting address}
+@!debug
+print(' (',cur_font:1,')',c:1,'[',m1:1,',',m2:1,']');
+gubed@/
+ @<Stow the |boc| or |boc1| information@>;
+ @<Stow the glyph details@>;
+ end;
+ end;
+until o=post;
+
+@ @<Read the |boc| information@>=
+begin
+char_code:=gf_signed_quad;
+p:=gf_signed_quad;
+c:=char_code mod 256;
+if c<0 then c:=c+256;
+@!debug
+print('[',c:1,']');
+if char_code<>c then
+ print(' in family ',(char_code-c) div 256 : 1);
+gubed@/
+min_m:=gf_signed_quad; max_m:=gf_signed_quad;
+min_n:=gf_signed_quad; max_n:=gf_signed_quad;
+del_m:=max_m-min_m;
+del_n:=max_n-min_n;
+end
+
+@ @<Read the |boc1| information@>=
+begin
+char_code:=gf_byte;
+p:=-1;
+c:=char_code;
+del_m:=gf_byte; max_m:=gf_byte;
+del_n:=gf_byte; max_n:=gf_byte;
+min_m:=max_m-del_m;
+end
+
+@ @<Stow the |boc| or |boc1| information@>=
+stow_signed_pair(del_m+1);
+stow_signed_pair(-min_m); {this is the initial |m| value}
+stow_signed_pair(del_n+1);
+stow_signed_pair(max_n);
+
+@ @<Pass over the raster details@>= {this glyph has been downloaded}
+begin
+o:=gf_byte;
+while o<>eoc do begin
+ a:=cur_gf_loc;
+ while (o<paint1) or (o=skip0) or ((o>=new_row_0) and (o<=new_row_164)) do
+ o:=gf_byte;
+ if (o=paint1) or (o=skip1) then begin
+ p:=gf_byte; o:=gf_byte;
+ end
+ else if (o=paint2) or (o=skip2) then begin
+ p:=gf_byte; p:=gf_byte; o:=gf_byte;
+ end
+ else if o=xxx1 then begin {\MF\ will not do this but it is allowed}
+ p:=gf_byte;
+ while p>0 do begin q:=gf_byte; decr(p); end;
+ o:=gf_byte;
+ end;
+ end;
+end
+
+@* Downloading glyph information.
+As mentioned earlier, the information for each used glyph (as stored in
+the |mm_store| array) will have to be translated and downloaded by means of
+an |im_bgly| command on the first occasion that the glyph is to be
+printed. The following definitions and tables will assist in this work:
+
+@d advance_q==begin if q2<m2_max then incr(q2)
+ else
+ begin
+ q2:=4; {|-4<m2<4| is left free for other uses}
+ if q1<m1_max then incr(q1) else q1:=0;
+ end;
+ end
+
+@<Glob...@>=
+@!atab:array[1..8] of integer; {used to locate asterisks if showing pattern}
+@!btab:array[0..8] of integer; {used to define bits to blacken}
+
+@ @<Set initial values@>=
+atab[1]:=128;
+btab[0]:=255;
+for i:=2 to 8 do atab[i]:=atab[i-1] div 2;
+for i:=1 to 8 do btab[i]:=btab[i-1] div 2;
+
+@ We will also have occasion to read halfwords from |mm_store|.
+
+@p function read_signed_pair(mm_tmp:integer):integer;
+ {returns the next two bytes, signed}
+var a,b:eight_bits;
+m1_tmp,m2_tmp:integer;
+begin
+m1_tmp:=mm_tmp div (m2_size); m2_tmp:=mm_tmp mod (m2_size);
+a:=mm_store[m1_tmp,m2_tmp];
+if m2_tmp<m2_max then incr(m2_tmp)
+else
+ begin m2_tmp:=4;
+ if m1_tmp<m1_max then incr(m1_tmp) else m1_tmp:=0; {wrap-around assumed}
+ end;
+b:=mm_store[m1_tmp,m2_tmp];
+if a<128 then read_signed_pair:=(a*256)+b
+else read_signed_pair:=(a-256)*256+b;
+end;
+
+@ For debugging purposes it may be desirable to display the actual glyph
+raster while it is being downloaded.
+
+@p procedure show_it(v:integer);
+var i: integer;
+begin
+for i:=1 to 8 do
+ if v>=atab[i] then
+ begin
+ print('*'); v:=v-atab[i];
+ end else print('.');
+end;
+
+@ And here is the procedure that does the actual downloading.
+
+@p procedure do_im_bgly(@!c:integer);
+var b,dis,n,i,q,val,w,real_w:integer;
+q1,q2: integer;
+bytes_required:integer; {bytes per row for current glyph}
+begin
+im_byte(im_bgly);
+if c<128 then im_halfword(cur_font*128+c) {normal family and member name}
+else im_halfword(im_extension[cur_font]*128+c-128);
+ {Imagen's family and member name}
+q:=pixel_width[data_base[cur_font]+c];
+im_halfword(q); {advance width}
+q:=glyph_ptr[data_base[cur_font]+c];
+ {get starting location in |mm_store|}
+q1:= q div (m2_size); q2:=q mod (m2_size);
+@!debug
+print(' im(',cur_font:1,')',c:1,'[',q1:1,',',q2:1,']');
+gubed@/
+bytes_required:=((read_signed_pair(q)+7)div 8);
+for i:=1 to 8 do
+ begin
+ im_byte(mm_store[q1,q2]);
+ advance_q;
+ end; {width, left offset, height,top offset}
+n:=0; dis:=0; val:=0; w:=0; real_w:=0;
+while real_w<>eoc do begin
+ @<Translate a sequence of paint commands@>;
+ w:=mm_store[q1,q2];
+ real_w:=w;
+ if (w>=new_row_0) and (w<=new_row_164) then
+ @<Translate a |new_row| command@>
+ else if (w>=skip0) and (w<new_row_0) then
+ @<Translate a |skip| command@>
+else if real_w<>eoc then
+print_ln('BAD D L COM ',w:1,' (',cur_font:1,')',c:1,'[',q1:1,',',q2:1,']');
+ end;
+{|print_ln('G EOC');|}
+glyph_ptr[data_base[cur_font]+c]:=-glyph_ptr[data_base[cur_font]+c];
+ {to show that the glyph has been downloaded}
+end;
+
+@ @<Translate a sequence of paint commands@>=
+while n<bytes_required do begin
+ if dis=0 then begin
+ @<Get two paint commands@>; dis:=w+b;
+ end;
+ while dis<8 do begin
+ val:=val+btab[w]-btab[dis];
+ @<Get two paint commands@>; w:=dis+w; dis:=w+b;
+ end;
+ if w>=8 then w:=w-8
+ else begin
+ val:=val+btab[w]; w:=0;
+ end;
+ im_byte(val); dis:=dis-8;
+ val:=0; incr(n);
+ end
+
+@ @<Translate a |new_row| command@>=
+begin
+w:=w-new_row_0;
+advance_q;
+b:=mm_store[q1,q2];
+if b<=paint2 then begin
+ advance_q;
+ if b=paint2 then
+ begin b:=mm_store[q1,q2]; advance_q;
+ b:=b*256+mm_store[q1,q2]; advance_q;
+ end
+ else if b=paint1 then begin
+ b:=mm_store[q1,q2]; advance_q;
+ end;
+ n:=0; dis:=w+b; val:=0;
+ end
+else begin b:=0; w:=8*bytes_required; {a safety measure}
+ end;
+n:=0; dis:=w+b; val:=0;
+end
+
+@ @<Translate a |skip| command@>=
+begin
+if w>skip0 then begin
+ advance_q;
+ w:=mm_store[q1,q2];
+ while w>0 do begin
+ for n:=1 to bytes_required do im_byte(0);
+ decr(w);
+ end;
+ end;
+advance_q;
+n:=0; dis:=0; val:=0; w:=0; b:=0;
+end
+
+@ @<Get two paint commands@>=
+begin w:=mm_store[q1,q2];
+if w<=paint2 then
+ begin
+ if w=paint2 then
+ begin advance_q; w:=mm_store[q1,q2]; advance_q;
+ w:=w*256+mm_store[q1,q2]; {can be as high as 65535}
+ end
+ else if w=paint1 then
+ begin advance_q; w:=mm_store[q1,q2]; {can be between 64 and 255}
+ end;
+ advance_q;
+ b:=mm_store[q1,q2];
+ if b<=paint2 then
+ begin
+ if b=paint2 then
+ begin advance_q; b:=mm_store[q1,q2]; advance_q;
+ b:=b*256+mm_store[q1,q2];
+ end
+ else if b=paint1 then
+ begin advance_q;
+ b:=mm_store[q1,q2];
+ end;
+ advance_q;
+ end
+ else
+ begin
+ b:=0; w:=8*bytes_required; {a safety measure}
+ end;
+ end
+else
+ begin b:=0; w:=8*bytes_required; {a safety measure}
+ end;
+end
+
+
+
+@* Translation to Impress form.
+The main work of \.{DVIIMP} is accomplished by the |do_page| procedure,
+which produces the output for an entire page, assuming that the |bop|
+command for that page has already been processed. This procedure is
+essentially an interpretive routine that reads and acts on the \.{DVI}
+commands.
+
+@ The definition of \.{DVI} files refers 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 always true that |hh=pixel_round(h)| or
+|vv=pixel_round(v)|. We will also find it useful to have two other
+registers, |hhi| and |vvi|
+to hold the values that \.{IMAGEN} would automatically
+assign for for the horizontal and vertical locations.
+
+The stack of $(h,v,w,x,y,z)$ values is represented by eight arrays
+called |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|.
+
+@<Glob...@>=
+@!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!hhi,@!vv,@!vvi:integer; {current state values}
+@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack:
+ array [0..stack_size] of integer; {pushed down values in \.{DVI} units}
+@!hhstack,@!vvstack:
+ array [0..stack_size] of integer; {pushed down values in pixels}
+@!h_org, @!v_org: integer; {page origin}
+
+@ Three characteristics of the pages (their |max_v|, |max_h|, and
+|max_s|) are specified in the postamble.
+Only |max_s| should not be exceeded.
+The postamble also specifies the total number of pages.
+
+@<Glob...@>=
+@!max_v:integer; {the value of |abs(v)| should probably not exceed this}
+@!max_h:integer; {the value of |abs(h)| should probably not exceed this}
+@!max_s:integer; {the stack depth should not exceed this}
+@!max_s_so_far:integer; {the record high levels}
+@!total_pages:integer; {the stated total number of pages}
+
+@ @<Set init...@>=
+max_s:=stack_size+1;
+max_s_so_far:=0;
+
+@ Before we get into the details of |do_page|, it is convenient to
+consider a simpler routine that computes the first parameter of each
+opcode. In doing this, we will use some multiple-case terms that were
+defined earlier.
+
+@p function first_par(o:eight_bits):integer;
+begin case o of
+sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
+ first_par:=o-set_char_0;
+set1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte;
+set1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes;
+set1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes;
+right1,w1,x1,down1,y1,z1: first_par:=signed_byte;
+right1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair;
+right1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio;
+set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3,
+ fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad;
+nop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0;
+w0: first_par:=w;
+x0: first_par:=x;
+y0: first_par:=y;
+z0: first_par:=z;
+sixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0;
+end;
+end;
+
+@ Here is another subroutine that we need: It computes the number of
+pixels in the height or width of a rule. Characters and rules will line up
+properly if the sizes are computed precisely as specified here. (Since
+|conv| is computed with some floating-point roundoff error, in a
+machine-dependent way, format designers who are tailoring something for a
+particular resolution should not plan their measurements to come out to an
+exact integer number of pixels; they should compute things so that the
+rule dimensions are a little less than an integer number of pixels, e.g.,
+4.99 instead of 5.00.)
+
+@p function rule_pixels(x:integer):integer;
+ {computes $\lceil|conv|\cdot x\rceil$}
+var n:integer;
+begin n:=trunc(conv*x);
+if n<conv*x then rule_pixels:=n+1 @+ else rule_pixels:=n;
+end;
+
+@ The Imagen is capable of executing a limited repartee of graphic
+commands and it will be convenient to assign a set of six
+\.{\\special} commands to invoke them.
+We will need the following globals.
+
+@<Glob...@>=
+@!pen_size: integer; {must be between 0 and 20 finally}
+@!hh_point,@!vv_point:array[0..255] of integer; {point coordinates}
+@!p_index:integer; {used to index |hh_point| and |vv_point|}
+@!join_points:array[0..255] of eight_bits; {points used in a |join|}
+@!vertex_count:integer; {used to index |join_points|}
+@!xxx_point:array[1..6] of eight_bits;
+@!xxx_join:array[1..5] of eight_bits;
+@!xxx_rectangle:array[1..10] of eight_bits;
+@!xxx_circle:array[1..7] of eight_bits;
+@!xxx_arc:array[1..4] of eight_bits;
+@!xxx_segm:array[1..5] of eight_bits;
+@!xxx_ellipse:array[1..8] of eight_bits;
+@!xxx_o:eight_bits; {needed in special prcedures}
+@!xxx_k:integer; {needed in special prcedures}
+
+@ @<Set initial values@>=
+xxx_point[1]:="p"; xxx_point[2]:="o"; xxx_point[3]:="i"; xxx_point[4]:="n";
+xxx_point[5]:="t"; xxx_point[6]:=" ";
+xxx_join[1]:="j"; xxx_join[2]:="o"; xxx_join[3]:="i"; xxx_join[4]:="n";
+xxx_join[5]:=" ";
+xxx_rectangle[1]:="r"; xxx_rectangle[2]:="e"; xxx_rectangle[3]:="c";
+xxx_rectangle[4]:="t"; xxx_rectangle[5]:="a"; xxx_rectangle[6]:="n";
+xxx_rectangle[7]:="g"; xxx_rectangle[8]:="l"; xxx_rectangle[9]:="e";
+xxx_rectangle[10]:=" ";
+xxx_circle[1]:="c"; xxx_circle[2]:="i"; xxx_circle[3]:="r"; xxx_circle[4]:="c";
+xxx_circle[5]:="l"; xxx_circle[6]:="e"; xxx_circle[7]:=" ";
+xxx_arc[1]:="a"; xxx_arc[2]:="r"; xxx_arc[3]:="c"; xxx_arc[4]:=" ";
+xxx_segm[1]:="s"; xxx_segm[2]:="e"; xxx_segm[3]:="g"; xxx_segm[4]:="m";
+xxx_segm[5]:=" ";
+xxx_ellipse[1]:="e"; xxx_ellipse[2]:="l";
+xxx_ellipse[3]:="l"; xxx_ellipse[4]:="i";
+xxx_ellipse[5]:="p"; xxx_ellipse[6]:="s";
+xxx_ellipse[7]:="e"; xxx_ellipse[8]:=" ";
+
+@ The following procedures will be used for these \.{\\special} commands.
+
+@p function read_ascii(p:integer):real;
+var jj,kk:real;
+negative:boolean;
+begin
+jj:=0.0;
+negative:=false;
+while (xxx_o=" ") and (xxx_k<p) do begin incr(xxx_k); xxx_o:=get_byte; end;
+if (xxx_o="-") and (xxx_k<p) then
+ begin negative:=true;
+ incr(xxx_k); xxx_o:=get_byte;
+ end;
+while (xxx_o>="0") and (xxx_o<="9") and (xxx_k<=p) do
+ begin
+ jj:=jj*10+(xxx_o-"0"); incr(xxx_k);
+ if xxx_k<=p then xxx_o:=get_byte;
+ end;
+if (xxx_o=".") and (xxx_k<p) then
+ begin
+ incr(xxx_k); xxx_o:=get_byte;
+ kk:=1.0;
+ while (xxx_o>="0") and (xxx_o<="9") and (xxx_k<=p) do
+ begin
+ kk:=kk*0.1; jj:=jj+kk*(xxx_o-"0"); incr(xxx_k);
+ if xxx_k<=p then xxx_o:=get_byte;
+ end;
+ end;
+if negative then jj:=-jj;
+read_ascii:=jj;
+end;
+
+@ This procedure defines the points for use by the |do_join| procedure
+that follows.
+
+@p procedure do_point(p:integer);
+var k:integer; {loop variable}
+o:eight_bits;
+match:boolean; {does everything match}
+begin if p<7 then for k:=2 to p do o:=get_byte else
+ begin match:=true;
+ for k:=2 to 6 do
+ begin o:=get_byte;
+ if o<>xxx_point[k] then match:=false;
+@!debug
+ print(xchr[o]);
+gubed
+ end;
+ p_index:=0;
+ for k:=7 to p do
+ begin o:=get_byte;
+ if match then p_index:=p_index*10+o-"0";
+ end;
+ if match then
+ begin hh_point[p_index]:=pixel_round(h);
+ vv_point[p_index]:=pixel_round(v);
+@!debug
+print(p_index:1,' ',pixel_round(h):1,',',pixel_round(v):1);
+gubed
+ end;
+ end;
+end;
+
+@ The |do_join| procedure joins points by straight lines only.
+
+@p procedure do_join(p:integer);
+var k,q:integer;
+jj:real; {used in computing |pen_size|}
+match:boolean; {does everything match}
+begin if p<8 then for k:=2 to p do xxx_o:=get_byte else
+ begin match:=true;
+ for k:=2 to 5 do
+ begin xxx_o:=get_byte;
+ if xxx_o<>xxx_join[k] then match:=false;
+ end;
+ if not match then for k:=6 to p do xxx_o:=get_byte else
+ begin xxx_o:=get_byte;
+ xxx_k:=6;
+ jj:=read_ascii(p);
+ pen_size:=pixel_round(jj*65536.0);
+ if pen_size>20 then pen_size:=20 else if pen_size<0 then pen_size:=0;
+ im_byte(set_pen); im_byte(pen_size);
+ vertex_count:=1; q:=0; incr(xxx_k);
+ for k:=xxx_k to p do begin
+ xxx_o:=get_byte;
+ if (xxx_o>="0") and (xxx_o<="9") then q:=q*10+xxx_o-"0" else
+ if xxx_o=" " then begin
+ join_points[vertex_count]:=q; incr(vertex_count); q:=0;
+ end;
+ end;
+ join_points[vertex_count]:=q;
+ im_byte(create_path);
+ im_halfword(vertex_count);
+ for q:=1 to vertex_count do
+ begin im_halfword(hh_point[join_points[q]]);
+ im_halfword(vv_point[join_points[q]]);
+ end;
+ im_byte(draw_path); im_byte(15);
+ end;
+ end;
+end;
+
+@ And now we come the the |do_circle| procedure.
+
+@p procedure do_circle(p:integer);
+var k,q,r:integer; jj:real;
+match:boolean; {does everything match}
+begin if p<13 then for k:=2 to p do xxx_o:=get_byte else
+ begin match:=true;
+ for k:=2 to 7 do
+ begin xxx_o:=get_byte;
+ if xxx_o<>xxx_circle[k] then match:=false;
+@!debug
+ print(xchr[xxx_o]);
+gubed
+ end;
+ if not match then for k:=8 to p do xxx_o:=get_byte else
+ begin xxx_o:=get_byte;
+ xxx_k:=8;
+ jj:=read_ascii(p);
+ pen_size:=pixel_round(jj*65536.0);
+ if pen_size>20 then pen_size:=20 else if pen_size<0 then pen_size:=0;
+ im_byte(set_pen); im_byte(pen_size);
+ @<Resyncronize@>;
+ im_byte(circ_arc);
+@!debug
+ print('(',pen_size:1,')');
+gubed
+ jj:=read_ascii(p);
+ r:=pixel_round(jj*65536.0); im_halfword(r); {the radius}
+@!debug
+ print('(',r:1,')');
+gubed
+ jj:=read_ascii(p);
+ q:=-round(jj*16384/360); {to measure counterclockwise}
+ im_halfword(q); {first angle}
+@!debug
+ print('(',q:1,')');
+gubed
+ jj:=read_ascii(p);
+ r:=-round(jj*16384/360); {to measure counterclockwise}
+ im_halfword(r); {second angle}
+@!debug
+ print('(',r:1,')');
+gubed
+ im_byte(draw_path); im_byte(15);
+ end;
+ end;
+end;
+
+@ And finally the |do_ellipse| procedure.
+@p procedure do_ellipse(p:integer);
+var k,q,r:integer; jj:real;
+match:boolean; {does everything match}
+begin if p<18 then for k:=2 to p do xxx_o:=get_byte else
+ begin match:=true;
+ for k:=2 to 8 do
+ begin xxx_o:=get_byte;
+ if xxx_o<>xxx_ellipse[k] then match:=false;
+@!debug
+ print(xchr[xxx_o]);
+gubed
+ end;
+ if not match then for k:=9 to p do xxx_o:=get_byte else
+ begin xxx_o:=get_byte;
+ xxx_k:=9;
+ jj:=read_ascii(p);
+ pen_size:=pixel_round(jj*65536.0);
+ if pen_size>20 then pen_size:=20 else if pen_size<0 then pen_size:=0;
+ im_byte(set_pen); im_byte(pen_size);
+ @<Resyncronize@>;
+ im_byte(ellipse_arc);
+@!debug
+ print('(',pen_size:1,')');
+gubed
+ jj:=read_ascii(p);
+ r:=pixel_round(jj*65536.0); im_halfword(r); {radiusa}
+@!debug
+ print('(',r:1,')');
+gubed
+ jj:=read_ascii(p);
+ r:=pixel_round(jj*65536.0); im_halfword(r); {radiusb}
+@!debug
+ print('(',r:1,')');
+gubed
+ jj:=read_ascii(p);
+ q:=-round(jj*16384/360); {to measure counterclockwise}
+ im_halfword(q); {|alpha_offset|}
+@!debug
+ print('(',q:1,')');
+gubed
+ jj:=read_ascii(p);
+ q:=-round(jj*16384/360); {to measure counterclockwise}
+ im_halfword(q); {first angle}
+@!debug
+ print('(',q:1,')');
+gubed
+ jj:=read_ascii(p);
+ r:=-round(jj*16384/360); {to measure counterclockwise}
+ im_halfword(r); {second angle}
+@!debug
+ print('(',r:1,')');
+gubed
+ im_byte(draw_path); im_byte(15);
+ end;
+ end;
+end;
+
+@ The |do_page|
+subroutine is organized as a typical interpreter, with a multiway branch
+on the command code followed by |goto| statements leading to routines that
+finish up the activities common to different commands. We will use the
+following labels:
+
+@d fin_set=41 {label for commands that set or put a character}
+@d fin_rule=42 {label for commands that set or put a rule}
+@d move_right=43 {label for commands that change |h|}
+@d move_down=44 {label for commands that change |v|}
+@d change_font=45 {label for commands that change |cur_font|}
+
+@ Some \PASCAL\ compilers severely restrict the length of procedure bodies,
+so we shall split |do_page| into two parts, one of which is
+called |special_cases|. The different parts communicate with each other
+via the global variables mentioned above, together with the following ones:
+
+@<Glob...@>=
+@!s:integer; {current stack size}
+@!cur_font:integer; {current internal font number}
+
+@ Here is the overall setup.
+
+@d infinity==@'17777777777 {$\infty$ (approximately)}
+
+@p @t\4@>@<Declare the function called |special_cases|@>@;
+procedure do_page;
+label fin_set,fin_rule,move_right,done,9999;
+var o:eight_bits; {operation code of the current command}
+@!p,@!q:integer; {parameters of the current command}
+@!g:integer; {to hold |glyph_ptr| temporarily and force its computation}
+@!a:integer; {byte number of the current command}
+@!hhh:integer; {|h|, rounded to the nearest pixel}
+begin cur_font:=nf; {set current font undefined}
+ s:=0; w:=0; x:=0; y:=0; z:=0;
+ h:=round(h_org/conv); v:=round(v_org/conv);
+ hh:=pixel_round(h); vv:=pixel_round(v);
+ hhi:=infinity; vvi:=infinity;
+ {initialize the state variables}
+while true do @<Translate the next command in the \.{DVI} file;
+ |goto 9999| if it was |eop|@>;
+9999: im_byte(im_end_page);
+end;
+
+@ The following procedure will do the actual comparing of the specified
+|start_page| with values of |count[0]| and it will increment |f_count|.
+
+@p procedure back_count;
+var @!k:0..255; {command code}
+begin
+move_to_byte(new_backpointer);
+k:=get_byte; if k=bop then
+ begin
+ incr(f_count);
+ for k:=0 to 9 do count[k]:=signed_quad;
+ if count[0]=start_page then page_match:=true;
+ new_backpointer:=signed_quad;
+ end else new_backpointer:=-1;
+end;
+
+@ The following routine allows us to read the pages in reverse order.
+
+@p procedure next_page;
+var @!k:0..255; {command code}
+begin
+incr(counter);
+move_to_byte(new_backpointer);
+k:=get_byte; if k=bop then
+ begin
+ for k:=0 to 9 do count[k]:=signed_quad;
+ new_backpointer:=signed_quad;
+@!debug
+ print_ln(' In next_page first_backpointer=',first_backpointer:1);
+gubed
+ end;
+if (counter>=l_count) then
+ begin
+ do_page; print('[',count[0]:1,'] ');
+ end;
+end;
+
+@ The main command loop.
+
+@<Translate the next command...@>=
+begin a:=cur_loc;
+@!debug
+ print_nl; print(a:1,': ');
+gubed
+o:=get_byte; p:=first_par(o);
+if eof(dvi_file) then bad_dvi('the file ended prematurely');
+@.the file ended prematurely@>
+@<Start translation of command |o| and |goto| the appropriate label to
+ finish the job@>;
+fin_set: @<Finish a command that either sets or puts a character, then
+ |goto move_right| or |done|@>;
+fin_rule: @<Finish a command that either sets or puts a rule, then
+ |goto move_right| or |done|@>;
+move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>;
+done:
+end
+
+@ The multiway switch in |first_par|, above, was organized by the length
+of each command; the one in |do_page| is organized by the semantics.
+
+@<Start translation...@>=
+if o<set_char_0+128 then goto fin_set
+else case o of
+ four_cases(set1): goto fin_set;
+ four_cases(put1): goto fin_set;
+ set_rule: goto fin_rule;
+ put_rule: goto fin_rule;
+ @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
+ @t\4@>@<Cases for horizontal motion@>@;
+ othercases begin special_cases(o,p,a); goto done; end
+ endcases
+
+@ @<Declare the function called |special_cases|@>=
+procedure special_cases(@!o:eight_bits;@!p,@!a:integer);
+label change_font,move_down,done;
+var q:integer; {parameter of the current command}
+@!k:integer; {loop index}
+@!vvv:integer; {|v|, rounded to the nearest pixel}
+begin
+case o of
+@t\4@>@<Cases for vertical motion@>@;
+@t\4@>@<Cases for fonts@>@;
+four_cases(xxx1): @<Translate an |xxx| command and |goto done|@>;
+pre: bad_dvi('preamble command within a page!');
+@.preamble command within a page@>
+post,post_post: bad_dvi('postamble command within a page!');
+@.postamble command within a page@>
+othercases bad_dvi('undefined command ',o:1,'!')
+@.undefined command@>
+endcases;
+move_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>;
+change_font: @<Finish a command that changes the current font,
+ then |goto done|@>;
+done:
+end;
+
+@ @<Cases for commands |nop|, |bop|, \dots, |pop|@>=
+nop: goto done;
+bop: bad_dvi('bop occurred before eop!');
+@.bop occurred before eop@>
+eop: begin
+ if s<>0 then bad_dvi('stack not empty at end of page (level ',
+ s:1,')!');
+@.stack not empty...@>
+ goto 9999;
+ end;
+push: begin
+ if s=max_s_so_far then
+ begin max_s_so_far:=s+1;
+ if s=max_s then bad_dvi('deeper than claimed in postamble!');
+@.deeper than claimed...@>
+@.push deeper than claimed...@>
+ if s=stack_size then
+ bad_dvi('DVIIMP capacity exceeded (stack size=',
+ stack_size:1,')');
+ end;
+ hstack[s]:=h; vstack[s]:=v; wstack[s]:=w;
+ xstack[s]:=x; ystack[s]:=y; zstack[s]:=z;
+ hhstack[s]:=hh; vvstack[s]:=vv; incr(s);
+@!debug
+print(' push(',s:1,')',hh:1,',',vv:1);
+gubed
+ goto done;
+ end;
+pop: begin
+ if s=0 then bad_dvi('POP illegal at level zero')
+ else begin decr(s); hh:=hhstack[s]; vv:=vvstack[s];
+ h:=hstack[s]; v:=vstack[s]; w:=wstack[s];
+ x:=xstack[s]; y:=ystack[s]; z:=zstack[s];
+@!debug
+print(' pop(',s:1,')',hh:1,',',vv:1);
+gubed
+ end;
+ goto done;
+ end;
+
+@ Rounding to the nearest pixel is best done in the manner shown here, so as
+to be inoffensive to the eye: When the horizontal motion is small, like a
+kern, |hh| changes by rounding the kern; but when the motion is large, |hh|
+changes by rounding the true position |h| so that accumulated rounding errors
+disappear. We allow a larger space in the negative direction than in
+the positive one, because \TeX\ makes comparatively
+large backspaces when it positions accents.
+
+@d out_space==if (p>=font_space[cur_font])or(p<=-4*font_space[cur_font]) then
+ hh:=pixel_round(h+p)
+ else hh:=hh+pixel_round(p);
+ q:=p; goto move_right
+
+@<Cases for horizontal motion@>=
+four_cases(right1): begin out_space; end;
+w0,four_cases(w1):begin w:=p; out_space; end;
+x0,four_cases(x1):begin x:=p; out_space; end;
+
+@ Vertical motion is done similarly, but with the threshold between
+``small'' and ``large'' increased by a factor of five. The idea is to make
+fractions like ``$1\over2$'' round consistently, but to absorb accumulated
+rounding errors in the baseline-skip moves.
+
+@d out_vmove==if abs(p)>=5*font_space[cur_font] then vv:=pixel_round(v+p)
+ else vv:=vv+pixel_round(p);
+ goto move_down
+
+@<Cases for vertical motion@>=
+four_cases(down1): begin out_vmove; end;
+y0,four_cases(y1): begin y:=p; out_vmove; end;
+z0,four_cases(z1): begin z:=p; out_vmove; end;
+
+@ @<Cases for fonts@>=
+sixty_four_cases(fnt_num_0): goto change_font;
+four_cases(fnt1): goto change_font;
+four_cases(fnt_def1): begin skip_it; goto done; end;
+
+@ @<Translate an |xxx| command and |goto done|@>=
+begin
+if p<0 then bad_dvi('string of negative length!');
+@.string of negative length@>
+if p<=0 then goto done;
+o:=get_byte;
+case o of
+"p":begin
+@!debug
+ print_nl; print(a:1,': ');
+ print(' p');
+gubed
+ do_point(p);
+ end;
+"j":begin
+@!debug
+ print_nl; print(a:1,': ');
+ print(' j');
+gubed
+ do_join(p);
+ end;
+"c":begin
+@!debug
+ print_nl; print(a:1,': ');
+ print(' c');
+gubed
+ do_circle(p);
+ end;
+"e":begin
+@!debug
+ print_nl; print(a:1,': ');
+ print(' e');
+gubed
+ do_ellipse(p);
+ end;
+othercases begin print(' othercases');
+ for k:=2 to p do o:=get_byte;
+ end
+endcases;
+goto done;
+end
+
+@ @<Resyncronize@>=
+if hhi<>hh then begin
+@!debug
+print(' ',hhi:1,',',hh:1);
+gubed
+ hhi:=hh; im_byte(set_abs_h); im_halfword(hh);
+ end;
+if vvi<>vv then begin
+ vvi:=vv; im_byte(set_abs_v); im_halfword(vv);
+ end;
+
+@ @<Finish a command that either sets or puts a character...@>=
+if p<0 then p:=255-((-1-p) mod 256)
+else if p>=256 then p:=p mod 256; {width computation for oriental fonts}
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+{|if (p<font_bc[cur_font])or(p>font_ec[cur_font]) then q:=invalid_width else|}
+ q:=char_width(cur_font)(p);
+@!debug
+print_ln(' p=',p:1);
+print_ln(' bc=',font_bc[cur_font]:1,' ec=',font_ec[cur_font]:1);
+print(' ch',char_width(cur_font)(p):1);
+print_ln(' q=',q:1);
+gubed
+if q=invalid_width then
+ begin print('character ',p:1,' invalid in font ');
+@.character $c$ invalid...@>
+ print_font(cur_font);
+ if cur_font<>nf then print('!'); {font |nf| has `\.!' in its name}
+ end
+else begin
+ g:=glyph_ptr[data_base[cur_font]+p];
+@!debug
+if g<-3 then print(' (',cur_font:1,')',p:1);
+gubed
+ if g=0 then begin
+@!debug
+ print_ln(' must reload (',cur_font:1,')',p:1);
+gubed
+ reload_font; {font must be reloaded}
+ g:=glyph_ptr[data_base[cur_font]+p];
+ end;
+@!debug
+if g=-1 then print(' -1(',cur_font:1,')',p:1);
+gubed
+ if g>3 then do_im_bgly(p);
+ @<Resyncronize@>;
+if (font_state[cur_font]=2) or (g=-1) then
+ begin
+ hhi:=hhi+pixel_width[data_base[cur_font]+p];
+ @<Resyncronize@>;
+ end
+else
+ begin
+ if p<128 then im_byte(p) {this sets or puts p of current family}
+ else begin
+ im_byte(set_family); im_byte(im_extension[cur_font]);
+ im_byte(p-128); {this sets or puts glyph under its imagen name}
+ im_byte(set_family); im_byte(cur_font);
+ end;
+ hhi:=hhi+pixel_width[data_base[cur_font]+p];
+ end;
+ end;
+if o>=put1 then goto done;
+if q=invalid_width then q:=0
+else hh:=hh+char_pixel_width(cur_font)(p);
+goto move_right
+
+@ @<Finish a command that either sets or puts a rule...@>=
+q:=signed_quad;
+@<Resyncronize@>;
+im_byte(im_brule); im_halfword(rule_pixels(q)); im_halfword(rule_pixels(p));
+im_halfword(rule_pixels(-p));
+if o=put_rule then goto done;
+hh:=hh+rule_pixels(q);
+goto move_right
+
+@ A sequence of consecutive rules, or consecutive characters in a fixed-width
+font whose width is not an integer number of pixels, can cause |hh| to drift
+far away from a correctly rounded value. \.{DVIIMP} ensures that the
+amount of drift will never exceed |max_drift| pixels.
+
+@d max_drift=2 {we insist that abs|(hh-pixel_round(h))<=max_drift|}
+
+@<Finish a command that sets |h:=h+q|, then |goto done|@>=
+if (h>0)and(q>0) then if h>infinity-q then
+ begin print('arithmetic overflow! parameter changed from ',
+@.arithmetic overflow...@>
+ q:1,' to ',infinity-h:1);
+ q:=infinity-h;
+ end;
+if (h<0)and(q<0) then if -h>q+infinity then
+ begin print('arithmetic overflow! parameter changed from ',
+ q:1, ' to ',(-h)-infinity:1);
+ q:=(-h)-infinity;
+ end;
+hhh:=pixel_round(h+q);
+if abs(hhh-hh)>max_drift then
+ begin
+ if hhh>hh then hh:=hhh-max_drift
+ else hh:=hhh+max_drift;
+ hhi:=hh; im_byte(set_abs_h); im_halfword(hhi);
+ end;
+h:=h+q;
+@!debug
+print(' r ',hh:1,' ');
+gubed
+goto done
+
+@ @<Finish a command that sets |v:=v+p|, then |goto done|@>=
+if (v>0)and(p>0) then if v>infinity-p then
+ begin print('arithmetic overflow! parameter changed from ',
+@.arithmetic overflow...@>
+ p:1,' to ',infinity-v:1);
+ p:=infinity-v;
+ end;
+if (v<0)and(p<0) then if -v>p+infinity then
+ begin print('arithmetic overflow! parameter changed from ',
+ p:1, ' to ',(-v)-infinity:1);
+ p:=(-v)-infinity;
+ end;
+vvv:=pixel_round(v+p);
+if abs(vvv-vv)>max_drift then
+ begin
+ if vvv>vv then vv:=vvv-max_drift
+ else vv:=vvv+max_drift;
+ vvi:=vv; im_byte(set_abs_v); im_halfword(vvi);
+ end;
+v:=v+p;
+@!debug
+print(' d ',vv:1,' ');
+gubed
+goto done
+
+@ @<Finish a command that changes the current font...@>=
+font_num[nf]:=p; cur_font:=0;
+while font_num[cur_font]<>p do incr(cur_font);
+if cur_font=nf then bad_dvi('bad font?');
+if font_state[cur_font]=0 then
+ begin get_gf_file;
+ if font_state[cur_font]=0 then
+ begin
+ if in_gf(font_scaled_size[cur_font]) then
+ font_state[cur_font]:=1 else font_state[cur_font]:=-1;
+ end;
+ if font_state[cur_font]=2 then
+ begin
+ if in_tfm(font_scaled_size[cur_font]) then
+ font_state[cur_font]:=2 else font_state[cur_font]:=-1;
+ end;
+ end;
+im_byte(set_family); im_byte(cur_font);
+goto done
+
+@* Using the backpointers.
+The routines in this section of the program are brought into play only
+if |random_reading| is |true|.
+First comes a routine that illustrates how to find the postamble quickly.
+
+@<Find the postamble, working back from the end@>=
+n:=dvi_length;
+if n<53 then bad_dvi('only ',n:1,' bytes long');
+@.only n bytes long@>
+m:=n-4;
+repeat if m=0 then bad_dvi('all 223s');
+@.all 223s@>
+move_to_byte(m); k:=get_byte; decr(m);
+until k<>223;
+if k<>id_byte then bad_dvi('ID byte is ',k:1);
+@.ID byte is wrong@>
+move_to_byte(m-3); q:=signed_quad;
+if (q<0)or(q>m-33) then bad_dvi('post pointer ',q:1,' at byte ',m-3:1);
+@.post pointer is wrong@>
+move_to_byte(q); k:=get_byte;
+if k<>post then bad_dvi('byte ',q:1,' is not post');
+@.byte n is not post@>
+post_loc:=q; first_backpointer:=signed_quad;
+
+@ Note that the last steps of the above code save the locations of the
+the |post| byte and the final |bop|. We had better declare these global
+variables, together with others that we will need shortly.
+
+@<Glob...@>=
+@!post_loc:integer; {byte location where the postamble begins}
+@!first_backpointer:integer; {the pointer following |post|}
+@!new_backpointer:integer; {the current |bop| command location}
+
+@ The following routine locates the postamble in order to read the value
+of the |first_backpointer| but then processes the pages starting with the
+last page so that the pages will be stacked properly by the \.{IMAGEN}.
+
+@<Find the postamble then process the pages in reverse order@>=
+q:=post_loc;
+move_to_byte(q); k:=get_byte;
+if k<>post then bad_dvi('byte ',q:1,' is not post');
+@.byte n is not post@>
+first_backpointer:=signed_quad;
+new_backpointer:=first_backpointer;
+while (new_backpointer<>-1) and (counter<f_count) do next_page;
+while im_byte_no mod 4 <> 3 do im_byte(im_no_op);
+im_byte(im_eof);
+
+@* Reading the postamble.
+Now imagine that we are reading the \.{DVI} file and positioned just
+four bytes after the |post| command. That, in fact, is the situation,
+when the following part of \.{DVIIMP} is called upon to read, translate,
+and check the rest of the postamble.
+
+@p procedure read_postamble;
+var k:integer; {loop index}
+@!p:integer; {general purpose registers}
+begin
+post_loc:=cur_loc-5;
+if signed_quad<>numerator then
+ print_ln('numerator doesn''t match the preamble!');
+@.numerator doesn't match@>
+if signed_quad<>denominator then
+ print_ln('denominator doesn''t match the preamble!');
+@.denominator doesn't match@>
+if signed_quad<>mag then if new_mag=0 then
+ print_ln('magnification doesn''t match the preamble!');
+@.magnification doesn't match@>
+max_v:=signed_quad; max_h:=signed_quad;@/
+max_s:=get_two_bytes; total_pages:=get_two_bytes;@/
+@<Process the font definitions of the postamble@>;
+end;
+
+@ @<Process the font definitions...@>=
+repeat k:=get_byte;
+if (k>=fnt_def1)and(k<fnt_def1+4) then
+ begin p:=first_par(k);
+ identify_font(p); k:=nop;
+ end;
+until k<>nop;
+if k<>post_post then
+ print_ln('byte ',cur_loc-1:1,' is not postpost!')
+@.byte n is not postpost@>
+
+@ @<Establish range of pages to be printed@>=
+if f_flag=false then f_count:=total_pages else
+ begin f_count:=0; q:=post_loc;
+ move_to_byte(q); p:=get_byte;
+ page_match:=false; f_count:=0;
+ first_backpointer:=signed_quad;
+ new_backpointer:=first_backpointer;
+ while (new_backpointer<>-1) and (page_match=false) do back_count;
+ end;
+if n_flag=false then l_count:=1 else l_count:=f_count-num_pages+1;
+
+@* The main program.
+Now we are ready to put it all together. This is where \.{DVIIMP} starts,
+and where it ends.
+
+@p begin initialize; {get all variables initialized}
+@<Process the preamble@>;
+open_im_file;
+@<Find the postamble, working back from the end@>;
+read_postamble;
+@<Establish range of pages to be printed@>;
+@<Find the postamble then process the pages in reverse order@>;
+final_end:end.
+
+@ The main program needs a few global variables in order to do its work.
+
+@<Glob...@>=
+@!k,@!m,@!n,@!p,@!q:integer; {general purpose registers}
+@!id_len: 0..255;
+@!id: packed array[0..255] of 0..255;
+
+
+@ A \.{DVI}-reading program that reads the postamble first need not look at the
+preamble; but \.{DVIIMP} looks at the preamble in order to do error
+checking, and to display the introductory comment.
+
+@<Process the preamble@>=
+open_dvi_file;
+p:=get_byte; {fetch the first byte}
+if p<>pre then bad_dvi('First byte isn''t start of preamble!');
+@.First byte isn't...@>
+p:=get_byte; {fetch the identification byte}
+if p<>id_byte then
+ bad_dvi('identification in byte 1 should be ',id_byte:1,'!');
+@.identification...should be n@>
+@<Compute the conversion factor@>;
+id_len:=get_byte; {fetch the length of the introductory comment}
+p:=0;
+while p<id_len do
+ begin incr(p); id[p]:=get_byte;
+ end;
+
+@ The conversion factor |conv| is figured as follows: There are exactly
+|n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch,
+and |resolution| pixels per inch. Then we have to adjust this
+by the stated amount of magnification.
+
+@<Compute the conversion factor@>=
+numerator:=signed_quad; denominator:=signed_quad;
+if numerator<=0 then bad_dvi('numerator is ',numerator:1);
+@.numerator is wrong@>
+if denominator<=0 then bad_dvi('denominator is ',denominator:1);
+@.denominator is wrong@>
+conv:=(numerator/254000.0)*(resolution/denominator);
+mag:=signed_quad;
+if new_mag>0 then mag:=new_mag
+else if mag<=0 then bad_dvi('magnification is ',mag:1);
+@.magnification is wrong@>
+true_conv:=conv; conv:=true_conv*(mag/1000.0);
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{DVIIMP} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/dviware/dviimp/makefile b/dviware/dviimp/makefile
new file mode 100644
index 0000000000..d2feca6bee
--- /dev/null
+++ b/dviware/dviimp/makefile
@@ -0,0 +1,28 @@
+DESTDIR= /usr/local
+TEXSRC=/usr/src/local/TeX/tex82
+PFLAGS=-O
+PC = pc
+
+dviimp: dviimp.o diext.o
+ $(PC) $(PFLAGS) dviimp.o diext.o -o dviimp
+
+dviimp.o: dviimp.p diext.h
+ $(PC) $(PFLAGS) -c dviimp.p
+
+dviimp.p: dviimp.web dviimp.ch
+ tangle dviimp dviimp
+ pxp -O dviimp.p >tmp
+ mv tmp dviimp.p
+
+diext.o: diext.c
+ cc -O -I$(TEXSRC) -c diext.c
+
+luis: luis.c
+ cc -o luis luis.c
+
+clean:
+ rm *.o *.p *.pool
+
+install: dviimp
+ install -s dviimp $(DESTDIR)/dviimp
+
diff --git a/dviware/dviimp/specials.tex b/dviware/dviimp/specials.tex
new file mode 100644
index 0000000000..de17642bd4
--- /dev/null
+++ b/dviware/dviimp/specials.tex
@@ -0,0 +1,32 @@
+\documentstyle[12pt]{article}
+
+\def\Backslash{\char'134}
+
+\begin{document}
+These are the specials supported by {\em dviimp}, as near as I can tell. In all cases,
+distances and diameters are meassured in {\em pt}, and angles are measured in degrees.
+\begin{description}
+\item[{\tt \Backslash special\{point $n$\}}]\mbox{}\\
+This associates the current position on the page
+(established by \verb+\put+ or other positioning commands) with the point $n$. These
+points are used by the special \verb+join+ to draw lines.
+
+\item[{\tt \Backslash special\{join $p$ $n_1\mbox{ }\ldots\mbox{ }n_k$\}}]\mbox{}\\
+This connects a sequence
+of points established by the \verb+point+ special. The pen has diameter $p$. The points
+$n_1$ through $n_k$ are connected by $k-1$ lines.
+
+\item[{\tt \Backslash special\{circle $p$ $r$ $\theta_1$ $\theta_2$\}}]\mbox{}\\
+This draws a portion of a
+circle centered at the current position on the stage. The pen has diameter $p$, and the
+circle has radius $r$. The arc is drawn counter-clockwise from $\theta_1$ degrees to
+$\theta_2$ degrees. Negative angles are handled, but it is unknown how angles outside
+the range -360 to 360 degrees are treated.
+
+\item[{\tt \Backslash special\{ellipse $p$ $r_1$ $r_2$ $\alpha$ $\theta_1$ $\theta_2$\}}]\mbox{}\\
+The {\tt ellipse} special draws a portion of an ellipse. The pen diameter
+is $p$. The ellipse has radius $r_1$
+aligned at $\alpha$ degrees, and radius $r_2$ aligned at $\alpha+90$ degrees.
+The arc drawn counter-clockwise starts at $\alpha+\theta_1$ and ends at $\alpha+\theta_2$.
+\end{description}
+\end{document}
diff --git a/dviware/dviimp/warning b/dviware/dviimp/warning
new file mode 100644
index 0000000000..a435ecd902
--- /dev/null
+++ b/dviware/dviimp/warning
@@ -0,0 +1,15 @@
+This version of dviimp.web contains references to procedures
+read_h and read_v, whose function seems to be to reposition
+the output on the page. There is no equivalent for that
+operation in the Unix changefile, where it belongs in the
+command line parsing. This is chiefly because I cannot
+find where read_h and read_v are used in dviimp.web
+
+If anyone can fill out the missing functionality, please
+send the new change file to me mackay@june.cs.washington.edu
+and post the diffs from this changefile to TeXhax@SCORE.STANFORD.EDU.
+
+
+ Pierre A. MacKay
+ TUG Site Coordinator for
+ Unix-flavored TeX