summaryrefslogtreecommitdiff
path: root/dviware/laserjet
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/laserjet
Initial commit
Diffstat (limited to 'dviware/laserjet')
-rw-r--r--dviware/laserjet/dviplus.web2655
-rw-r--r--dviware/laserjet/readme22
2 files changed, 2677 insertions, 0 deletions
diff --git a/dviware/laserjet/dviplus.web b/dviware/laserjet/dviplus.web
new file mode 100644
index 0000000000..a47f87a932
--- /dev/null
+++ b/dviware/laserjet/dviplus.web
@@ -0,0 +1,2655 @@
+% DVIPLUS.WEB
+%
+% This program is not copyrighted and may be used freely.
+%
+% Written by Tor Lillqvist
+% Technical Research Centre of Finland
+% Lehtisaarentie 2 A
+% SF-00340 HELSINKI
+% FINLAND
+%
+% E-mail: tml@@fingate.bitnet, tml@@santra.uucp, ...!mcvax!santra!tml
+% Phone: +358 0 4566132
+% Telex: 122972 vttha sf
+
+% 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 SAIL
+\def\PASCAL{Pascal}
+
+\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{DVI$\,$\lowercase{plus}}
+\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 DVIplus} processor}
+ \vskip 10pt
+ \centerline{\titlefont for HP LaserJet+}
+ \vskip 15pt
+ \centerline{(Version 1.2, April 1986)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\baselineskip9pt
+ \vbox{\ninerm\noindent
+ `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+The \.{DVIplus} program reads binary device-independent (``\.{DVI}'')
+files that are produced by document compilers such as \TeX,
+and translates them for printing on a HP LaserJet+
+page printer.
+This program is written by Tor Lillqvist,
+based on the DVItype program by Donald E.~Knuth.
+
+This version is for the \PASCAL/1000 compiler on the RTE--A operating
+system running on the HP1000 A--Series computers.
+
+Programs for
+typesetting need to be especially careful about how they do arithmetic; if
+rounding errors accumulate, margins won't be straight, vertical rules
+won't line up, and so on. But if rounding is done everywhere, even in the
+midst of words, there will be uneven spacing between the letters, and that
+looks bad. Human eyes notice differences of a thousandth of an inch in the
+positioning of lines that are close together; on low resolution devices,
+where rounding produces effects four times as great as this, the problem
+is especially critical. Experience has shown that unusual care is needed
+even on high-resolution equipment; for example, a mistake in the sixth
+significant hexadecimal place of a constant once led to a difficult-to-find
+bug in some software for the Alphatype CRS, which has a resolution of 5333
+pixels per inch (make that 5333.33333333 pixels per inch). The document
+compilers that generate \.{DVI} files make certain assumptions about the
+arithmetic that will be used by \.{DVI}-reading software, and if these
+assumptions are violated the results will be of inferior quality.
+Therefore the present program is intended as a guide to proper procedure
+in the critical places where a bit of subtlety is involved.
+
+The |banner| string defined here should be changed whenever \.{DVIplus}
+gets modified.
+The editor of RTE--A automagically updates this timestamp when
+the file is written.
+
+@d banner=='This is DVIplus, RTE-A Version 1.2 <860605.2059>'
+
+@ This program is not written in standard \PASCAL, but
+the \PASCAL/1000 dialect used on HP1000 A--series computers
+running the RTE--A operating system;
+it should be easy to convert to other reasonable \PASCAL\
+dialects.
+
+Places where pecliarities depndent on the RTE--A implmentation have
+been used are 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.
+In fact, in RTE--A we cannot determine the logical length of a binary file,
+so another approach is used: The first doubleword in \.{PXL} and \.{DVI}
+files contains the size of the file (in doublewords = integers).
+The records are numbered sequentially starting from 1. The internal pointers
+in \.{DVI} and \.{PXL} files start the (byte) numbering from 0, so an offset
+is added in the |seek| calls.
+
+\PASCAL/1000 allows writing nonprinting characters to |text| files
+using a notation with a number sign followed by the decimal
+ordinal number of the character. For example, |write(f, #27'E')| writes
+an escape followed by a capital `E'.
+
+In \PASCAL/1000, output to |text| files is line buffered, and we cannot
+keep |write|ing without using |writeln| every now and then.
+This terminates the record, which
+normally causes a newline, but the terminal drivers in RTE--A
+leave the newline out if the last byte of a write request is underscore.
+This is handled by the |write_lj| macro.
+
+Another extension is to use a default |case| as in \.{TANGLE}, \.{WEAVE},
+etc.
+
+@d othercases == otherwise {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ Before the program heading we have some compiler options.
+These specify that code should be generated for the `CDS' mode,
+i.e. Code-and-Data-Separation.
+
+The binary input comes from |dvi_file| and the |pxl_file|s.
+@^system dependencies@>
+
+@p
+@=$Standard_Level 'HP1000', CDS On, Debug, Range Off$@>@/
+program DVIplus;
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<External routine declarations@>@/
+procedure initialize; {this procedure gets things started properly}
+var i:0..255;
+begin
+ @<Set initial values@>@/
+end;
+
+@ Here are some macros for common programming idioms.
+
+@d do_nothing == {empty statement}
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+
+@ We do our own error handling.
+@^system dependencies@>
+
+@d catch_errors==error_status:=true
+@d dont_catch_errors==error_status:=false
+
+@<Types...@>=
+@!error_type=(err_run,err_ema,err_io,err_fmp,err_seg,err_wrn);
+@!error_file_name=packed array [1..150] of char;
+
+@ The |error_status| flag is set |true| when errors are to be catched.
+If a file system related error occurs, |error_status| is set false.
+
+@<Glob...@>=
+@!error_status:boolean;
+
+@ If an error that we are not prepared to catch occurs, we call
+the standard error printing routine, and |halt|.
+
+@p procedure errorprinter @=$Alias 'Pas.ErrorPrinter'$@>
+(err_type: error_type; err_number, err_line: short;
+var err_file: error_file_name; err_flen: short);
+ external;@t\2@>@/
+@#
+procedure errorcatcher @=$Alias 'Pas.ErrorCatcher'$@>
+(err_type: error_type; err_number, err_line: short;
+var err_file: error_file_name; err_flen: short);
+begin
+ if err_type=err_wrn then
+ do_nothing
+ else if error_status and (err_type=err_fmp) then
+ error_status := false
+ else begin
+ errorprinter(err_type,err_number,err_line,err_file,err_flen);
+ halt(1); {an error we weren't prepared to catch}
+ end;
+end;
+
+@ External routines are declared here.
+|parameters| retrieves a runstring parameter.
+|LURQ| is used to lock the output device.
+|dcb_address| returns a pointer to the DCB (a sort of file control
+block for an open file.
+|fmp_interactive| tells if a DCB corresponds to an interactive device,
+and |fmp_lu| returns the `logical unit' number of a device.
+@^system dependencies@>
+
+@<External routine...@>=
+function parameters @=$Alias 'Pas.Parameters'$@>
+ (pos:short; var par:file_name; len:short):short; external; @t\2@>@#
+
+procedure LURQ(option:short; var lu:short; num: short; var key:short);
+ external; @t\2@>@#
+
+procedure dcb_address @=$Alias 'Pas.DcbAddress1'$@>
+ (var p:dcb_ptr; var f:text_file); external; @t\2@>@#
+
+function fmp_interactive @=$Alias 'FmpInteractive'$@>
+ (var d:dcb):short; external; @t\2@>@#
+
+function fmp_lu @=$Alias 'FmpLU'$@> (var d:dcb):short; external; @t\2@>@#
+
+@ Types related to system routines.
+
+@d name_length=64 {a file name shouldn't be longer than this}
+
+@<Types...@>=
+@!short=-32768..32767;
+@!file_name=packed array [1..name_length] of char;
+@!dcb=array [1..144] of short;
+@!dcb_ptr=^dcb;
+
+@ @<Globals...@>=
+@!laser_dcb:dcb_ptr;
+@!lu,@!key:short;
+
+@ We lock the printer after opening it if it is a device.
+This prevents intermixed output.
+The default output file name is given here.
+@^system dependencies@>
+
+@d default_out=='61'
+
+@<Open and lock |laser_file|@>=
+if parameters(arg_index,cur_name,name_length) <= 0 then
+ cur_name:=default_out;
+rewrite(laser_file,cur_name,'NOCCTL,SHARED');
+dcb_address(laser_dcb,laser_file);
+if fmp_interactive(laser_dcb^) <> 0 then begin
+ lu:=fmp_lu(laser_dcb^);
+ LURQ(1,lu,1,key);
+end
+
+@ Labels (global and local).
+
+@d done=30 {go here when finished with a subtask}
+@d exit=999 {go here to leave a procedure}
+@d return==goto exit
+
+@ The following parameters can be changed to extend or
+reduce \.{DVIplus}'s capacity.
+
+@d max_fonts=75 {maximum number of distinct fonts per \.{DVI} file}
+@d max_printer_fonts=20 {maximum number of fonts kept in printer}
+@d max_fonts_on_page=16 {maximum number of fonts per page}
+@d max_widths=9601 {|max_fonts * 128 + 1|}
+ {maximum number of different characters among all fonts}
+@d line_length=79 {bracketed lines of output will be at most this long}
+@d terminal_line_length=128 {maximum number of characters input in a single
+ line of input from the terminal}
+@d stack_size=100 {\.{DVI} files shouldn't |push| beyond this depth}
+@d name_size=1000 {total length of all font file names}
+@d max_bops=1000 {maximum number of pages printed}
+
+@ If the \.{DVI} file is badly malformed, the whole process must be aborted;
+\.{DVIplus} 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 simply
+halts the program using the \PASCAL/1000 procedure |halt|.
+@^system dependencies@>
+
+@d print(#)==write(term_out,#)
+@d print_ln(#)==write_ln(term_out,#)
+@d print_nl==write_ln(term_out)
+@d abort(#)==begin print('? ',#); jump_out;
+ end
+@d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
+@.Bad DVI file@>
+@d bad_pxl(#)==begin print_nl; abort('Bad PXL file: ',#,'!'); end
+@.Bad PXL file@>
+
+@p procedure jump_out;
+begin halt(1)
+end;
+
+@ We fill output lines to |laser_file|, breaking the line when
+|lj_threshold| characters have been written. An underscore character
+is appended to the line before the newline (record boundary).
+This prevents the RTE--A terminal driver from writing a {\tt CR~LF}
+pair at the end of the line.
+@^system dependencies@>
+
+All output to |laser_file| goes through the macros |write_lj| or
+|write_lj_f|. The latter doesn't test for line break before writing.
+
+@d lj_threshold=70
+
+@d write_lj(#)==begin if linepos(laser_file)>lj_threshold then
+ write_ln(laser_file,'_');
+ write(laser_file,#)
+end
+@d write_lj_f(#)==write(laser_file,#)
+
+@* The character set.
+Like all programs written with the \.{WEB} system, \.{DVIplus} 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 \.{DVIplus} have therefore been copied from the
+analogous ones in the \.{WEB} system routines. They have been considerably
+simplified, since \.{DVIplus} 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 \.{DVIplus}.
+So we shall assume that the \PASCAL\ system being used for \.{DVIplus}
+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=255 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=text_;
+
+@ The \.{DVIplus} 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;
+
+@ The LaserJet refuses to print other that `visible characters', that is
+characters in the range @'41--@'177 or @'240--@'377.
+
+@p function vis_chr(p : integer):char;
+begin
+ if p < @'41 then
+ p:=p+@'240;
+ vis_chr:=chr(p);
+end;
+
+@* Device-independent file format.
+Before we get into the details of \.{DVIplus}, 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.
+
+\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 \.{DVIplus} 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. \.{DVIplus} 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 dvi_id=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 \.{TFM} 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 \.{TFM} 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.
+
+@* Input from 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.
+@^system dependencies@>
+
+In the RTE--A implementation, \.{DVI} and \.{PXL} files are files of
+|integer|s,
+with the first |integer| containing the number of |integer|s to follow.
+(It is impossible to know the logical size of a random-access file).
+The bytes of the file are accessed through a variant record.
+
+@d pxl_id=1001 {identifies the kind of \.{PXL} files handled}
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+four=0..3;
+@!i2c=packed record case four of 0: (i:integer);@/
+1:(i0,i1:short);@/
+2:(b:packed array[0..3] of eight_bits);
+3:(c:packed array[0..3] of char) end;
+@!int_file=file of integer;
+@!byte_file=record {files that contain binary data}
+x:i2c;i:0..3;m,s:integer;
+f:int_file;
+end;
+
+@ The program deals with the binary file variables |dvi_file| which is the
+main input file that we are printing, and |pxl_file| is an array of
+font image files from which character images is downloaded
+to the printer.
+
+@<Glob...@>=
+@!laser_file:text_file;
+@!dvi_file:byte_file; {the stuff we are \.{DVI}typing}
+@!pxl_file:array[0..max_fonts] of file of i2c; {font image files}
+@!s_pxl_file:integer; {size of |pxl_file|}
+
+@ To prepare these files for input, we |reset| them. An extension of
+\PASCAL\ is needed in the case of |pxl_file|, since we want to associate
+it 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.
+The function result is false if the file couldn't be opened.
+@^system dependencies@>
+
+@p function open_dvi_file:boolean;
+var i,j:short;
+begin
+i:=parameters(arg_index,cur_name,name_length);
+incr(arg_index);
+j:=1;
+while (j<=i)and(cur_name[j]<>'.') do incr(j);
+if j>i then begin
+ cur_name[j]:='.'; incr(j);
+ cur_name[j]:='d'; incr(j);
+ cur_name[j]:='v'; incr(j);
+ cur_name[j]:='i'; incr(j);
+end;
+catch_errors;
+reset(dvi_file.f,cur_name,'SHARED');
+open_dvi_file:=error_status;
+if error_status then begin
+ dont_catch_errors;
+ close(dvi_file.f);
+ open(dvi_file.f,cur_name,'SHARED');
+ get(dvi_file.f);
+ dvi_file.s:=dvi_file.f^;
+ get(dvi_file.f);
+ dvi_file.x.i:=dvi_file.f^;
+ dvi_file.i:=0;
+end;
+cur_loc:=0;
+end;
+@#
+function open_pxl_file(i:short):boolean;
+begin
+catch_errors;
+reset(pxl_file[i],cur_name,'SHARED');
+open_pxl_file:=error_status;
+if error_status then begin
+ dont_catch_errors;
+ close(pxl_file[i]);
+ open(pxl_file[i],cur_name,'SHARED');
+ get(pxl_file[i]);
+ s_pxl_file:=pxl_file[i]^.i;
+ get(pxl_file[i]);
+ if pxl_file[i]^.i <> pxl_id then
+ bad_pxl('bad header id');
+end;
+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
+current pixel image file name before |open_pxl_file| is called.
+
+@<Glob...@>=
+@!cur_loc:integer; {where we are about to look, in |dvi_file|}
+@!cur_name:file_name; {external name,
+ with no lower case letters}
+
+@ It turns out to be convenient to read four bytes at a time, when we are
+inputting from \.{PXL} files. The input goes into global variables
+|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
+the fourth.
+
+@<Glob...@>=
+@!b0,@!b1,@!b2,@!b3: integer; {four bytes input at once}
+
+@ The |read_pxl_word| procedure sets |b0| through |b3| to the next
+four bytes in the current \.{TFM} file.
+@^system dependencies@>
+
+@p procedure read_pxl_word(i:short);
+begin
+b0:=pxl_file[i]^.b[0];b1:=pxl_file[i]^.b[1];
+b2:=pxl_file[i]^.b[2];b3:=pxl_file[i]^.b[3];
+get(pxl_file[i]);
+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@>
+
+This is the best way to check ``eof'' in RTE--A, as the logical length of
+the file is unknown (except from the first word).
+
+@d eof_dvi_file==(cur_loc>dvi_file.s*4)
+
+@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
+ get_byte:=dvi_file.x.b[dvi_file.i];
+ if dvi_file.i=3 then begin
+ get(dvi_file.f);
+ dvi_file.x.i:=dvi_file.f^;
+ dvi_file.i:=0; end
+ else
+ incr(dvi_file.i);
+ end;
+ incr(cur_loc);
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:eight_bits;
+begin b:=get_byte;
+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:integer;
+begin a:=get_byte; b:=get_byte;
+get_two_bytes:=a*256+b;
+end;
+@#
+function signed_pair:integer; {returns the next two bytes, signed}
+var a,@!b:integer;
+begin a:=get_byte; b:=get_byte;
+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 a:=get_byte; b:=get_byte; c:=get_byte;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_trio:integer; {returns the next three bytes, signed}
+var a,@!b,@!c:integer;
+begin a:=get_byte; b:=get_byte; c:=get_byte;
+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:integer;
+begin a:=get_byte; b:=get_byte; c:=get_byte; d:=get_byte;
+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 to acces the |dvi_file|
+randomly. 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.
+
+@d dvi_length==(dvi_file.s*4)
+@#
+@d move_to_byte(#)==begin seek(dvi_file.f,((#) div 4)+2); get(dvi_file.f);
+dvi_file.x.i:=dvi_file.f^; dvi_file.i:=(#) mod 4; cur_loc:=#; end
+
+@
+Font files
+should contain exactly the same character width data that is
+found in the corresponding \.{TFM}s; check sums are used to help
+ensure this. In addition, font files also contain the widths of
+characters in pixels, since the device-independent character widths of
+\.{TFM} files are generally not perfect multiples of pixels.
+
+The |pixel_width| array contains this information; when |width[k]| is the
+device-independent width of some character in \.{DVI} units, |pixel_width[k]|
+is the corresponding width of that character in an actual font.
+The macro |char_pixel_width| is set up to be analogous to |char_width|.
+
+The |status| array tells if a charater has been downloaded to the
+laser printer, or if it is too large, and must be transferred as raster
+graphics. It might also contain a positive value that indicates a shift
+up of the reference point (because the ``baseline'' of the characters
+is set at |baseline| pixels from the bottom, characters can't be deeper than
+|baseline| pixels).
+
+The large arrays are located in
+EMA (Extended Memory Area) (outside the 32 page range addressable
+with one-word addresses).
+The \.{Ema\_Var} compiler options specify which variables are in EMA.
+@^system dependencies@>
+
+@d char_end_width(#)==#]
+@d char_pixel_width(#)==pixel_width[width_base[#]+char_end_width
+@d char_status(#)==status[width_base[#]+char_end_width
+@d not_loaded=0
+@d too_large=-1
+@d loaded_ok=-2
+@d baseline=55
+
+@<Glob...@>=
+@=$Ema_Var On$@>
+@!pixel_width:array[0..max_widths] of short; {actual character widths,
+ in pixels}
+@!status:array[0..max_widths] of short; {character statuses}
+@=$Ema_Var Off$@>
+@!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}
+@!desired_mag:integer;
+
+@* Reading the font information.
+The current number of known fonts is |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. A horizontal motion
+in the range |-4*font_space[f]<h<font_space[f]|
+will be treated as a `kern'.
+A given character |c| is valid in font |f| if and only if
+|char_width(f)(c)<>invalid_width|.
+Finally, |char_width(f)(c)=width[width_base[f]+c]|, and |width_ptr| is the
+first unused position of the |width| array.
+
+@d char_width(#)==width[width_base[#]+char_end_width
+@d invalid_width==@'17777777777
+
+@<Glob...@>=
+@!font_num:array [0..max_fonts] of integer; {external font numbers}
+@!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_mag:array [0..max_fonts] of integer;
+@!font_space:array [0..max_fonts] of integer; {boundary between ``small''
+ and ``large'' spaces}
+@!font_used_on:array[0..max_fonts] of integer; {on which page last used}
+@!width_base:array [0..max_fonts] of integer; {index into |width| table}
+@=$Ema_Var On$@>
+@!width:array [0..max_widths] of integer; {character widths, in \.{DVI} units}
+@=$Ema_Var Off$@>
+@!nf:0..max_fonts; {the number of known fonts}
+@!width_ptr:0..max_widths; {the number of known character widths}
+
+@ @<Set init...@>=
+nf:=0; width_ptr:=0; font_name[0]:=0; font_space[0]:=0;
+font_used_on[0]:=0;
+
+@ 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 global variabls |pxl_check_sum|,
+|pxl_design_size| are set from the \.{PXL} file.
+
+@<Glob...@>=
+@!pxl_check_sum:integer; {check sum found in |pxl_file|}
+@!pxl_dptr:array [0..max_fonts] of integer; {directory pointers}
+@!pxl_design_size:integer;
+
+@ Here is a procedure that absorbs the necessary information from a
+\.{PXL} file, assuming that the file has just been successfully opened.
+(A complete description of
+\.{PXL} file format appears elsewhere and will
+not be repeated here.) The procedure does not check the \.{PXL} file
+for validity, nor does it give explicit information about what is
+wrong with a \.{PXL} file that proves to be invalid; \.{DVI}-reading
+programs need not do this, since \.{PXL} files are almost always valid.
+The procedure simply returns |false| if it
+detects anything amiss in the \.{PXL} data.
+
+There is a parameter, |z|, which represents the scaling factor being
+used to compute the font dimensions; it must be in the range $0<z<2^{27}$.
+
+@d read_pxl(#)==begin #:=pxl_file[nf]^.i; get(pxl_file[nf]); end
+
+@p procedure in_PXL(@!z:integer); {input \.{PXL} data}
+var i,j,k,n:short; {indices etc.}
+@!wp:0..max_widths; {new value of |width_ptr| after successful input}
+@!raster_address:integer;
+@!alpha,@!beta:integer; {quantities used in the scaling computation}
+begin
+ if width_ptr+128>max_widths then begin
+ print_nl;
+ abort('Need larger width table');
+ end;
+ width_base[nf]:=width_ptr;
+ wp:=width_ptr;
+ @<Check the header ID@>;
+ @<Read the trailer@>;
+ @<Read the font directory@>;
+ width_ptr:=wp;
+end;
+
+@ @<Check the header ID@>=
+begin
+ seek_pxl(nf)(0);
+ get(pxl_file[nf]);
+ if pxl_file[nf]^.i <> pxl_id then
+ bad_pxl('bad header id');
+end
+
+@ @<Read the trailer@>=
+begin
+ seek(pxl_file[nf],s_pxl_file+1);
+ get(pxl_file[nf]);
+ if pxl_file[nf]^.i <> pxl_id then
+ bad_pxl('bad trailer id');
+ seek(pxl_file[nf],s_pxl_file-3);
+ get(pxl_file[nf]);
+ read_pxl(pxl_check_sum);
+ read_pxl(font_mag[nf]);
+ read_pxl(pxl_design_size);
+ read_pxl(pxl_dptr[nf]);
+end;
+
+@ One important part of |in_PXL| is the width computation, which
+involves multiplying the relative widths in the \.{PXL} 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 \.{PXL}
+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 \.{TFM}
+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.
+
+The following code computes pixel widths by simply rounding the \.{PXL}
+widths to the nearest integer number of pixels, based on the conversion factor
+|conv| that converts \.{DVI} units to pixels. However, such a simple
+formula will not be valid for all fonts, and it will often give results that
+are off by $\pm1$ when a low-resolution font has been carefully
+hand-fitted. For example, a font designer often wants to make the letter `m'
+a pixel wider or narrower in order to make the font appear more consistent.
+\.{DVI}-to-printer programs should therefore input the correct pixel width
+information from font files whenever there is a chance that it may differ.
+A warning message may also be desirable in the case that at least one character
+is found whose pixel width differs from |conv*width| by more than a full pixel.
+
+Those characters that are too large to be downloadable, are marked as such,
+and will be transferred using raster graphics. We must be especially careful
+in the y dimension, as the character must fit into the 255*255 box when
+the reference point is placed on the baseline. The baseline was set to
+$255-$|baseline| pixel rows down from the top.
+@^system dependencies@>
+
+@d pixel_round(#)==round(conv*(#))
+
+@<Read the font dir...@>=
+@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
+for k:=0 to 127 do begin
+ seek(pxl_file[nf],pxl_dptr[nf]+k*4+2);
+ get(pxl_file[nf]);
+ {is the charater too large ?}
+ if (pxl_file[nf]^.i0 >= 128) or (pxl_file[nf]^.i1 >= 128) then
+ status[wp]:=too_large
+ else
+ status[wp]:=not_loaded;
+ get(pxl_file[nf]);
+ get(pxl_file[nf]);
+ if pxl_file[nf]^.i=0 then begin
+ width[wp]:=invalid_width;
+ pixel_width[wp]:=0 end
+ else begin
+ get(pxl_file[nf]);
+ read_pxl_word(nf);
+ width[wp]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
+ if b0>0 then if b0<255 then bad_pxl('strange width for char ', k:1)
+ else width[wp]:=width[wp]-alpha;
+ pixel_width[wp]:=pixel_round(width[wp]);
+ end;
+ incr(wp);
+end
+
+@ @<Replace |z|...@>=
+begin alpha:=16*z; beta:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; beta:=beta div 2;
+ end;
+end
+
+@* Downloading information to the printer.
+The procedure |update_pos| is used to update the printer cursor position.
+This is the only place where the cursor is explicitely positioned.
+
+@p procedure update_pos;
+label done;
+begin if (lj_h<>hh)or(lj_v<>vv) then begin
+ write_lj(@=#27'*p'@>);
+ if (lj_h<>hh) then begin
+ if abs(lj_h-hh) > hh div 10 then
+ write_lj_f(hh:1)
+ else if (lj_h<hh) then
+ write_lj_f('+', hh-lj_h:1)
+ else
+ write_lj_f('-', lj_h-hh:1);
+ if (lj_v<>vv) then
+ write_lj_f('x')
+ else begin
+ write_lj_f('X');
+ goto done;
+ end
+ end;
+ if abs(lj_v-vv) > vv div 10 then
+ write_lj_f(vv:1)
+ else if (lj_v<vv) then
+ write_lj_f('+', vv-lj_v:1)
+ else
+ write_lj_f('-', lj_v-vv:1);
+ write_lj_f('Y');
+end;
+done:
+lj_h:=hh; lj_v:=vv;
+end;
+
+@ This procedure downloads a single character from a \.{PXL} file.
+@^system dependencies@>
+
+@p procedure download_char(p:integer);
+var i,j:short;
+@!pixel_wd,@!pixel_ht:integer;
+@!offsets,char_wd,char_ht,delta_x:i2c;
+@!raster_address:integer;
+
+begin
+ seek(pxl_file[cur_font],pxl_dptr[cur_font]+p*4+2);
+ get(pxl_file[cur_font]);
+ pixel_wd:=pxl_file[cur_font]^.i0;
+ pixel_ht:=pxl_file[cur_font]^.i1;
+ get(pxl_file[cur_font]);
+ offsets:=pxl_file[cur_font]^;
+ get(pxl_file[cur_font]);
+ raster_address:=pxl_file[cur_font]^.i;
+ @<Download character header@>;
+ seek(pxl_file[cur_font],raster_address+2);
+ for j:=1 to pixel_ht do begin
+ for i:=1 to (pixel_wd-1) div 8 + 1 do begin
+ if (i-1) mod 4 = 0 then
+ get(pxl_file[cur_font]);
+ write_lj(pxl_file[cur_font]^.c[(i-1) mod 4]);
+ end;
+ end
+end;
+
+@ Download a character header.
+
+@d data_bytes==(pixel_ht*(((pixel_wd-1) div 8)+1))
+
+@<Download character header@>=
+begin
+ if pixel_ht - offsets.i1 > baseline then begin
+ char_status(cur_font)(p):=pixel_ht-offsets.i1;
+ offsets.i1:=pixel_ht; end
+ else
+ char_status(cur_font)(p):=loaded_ok;
+ offsets.i0:=-offsets.i0;
+ char_wd.i0:=pixel_wd;
+ char_ht.i0:=pixel_ht;
+ delta_x.i0:=char_pixel_width(cur_font)(p)*4;
+ write_lj(@=#27'*c'@>,font_num[cur_font]:1,'d',
+ ord(vis_chr(p)):1,@='E'#27'(s'@>,
+ data_bytes+16:1,'W'+
+ @=#4#0#14#1#0#0@>,
+ offsets.c[0], offsets.c[1],
+ offsets.c[2], offsets.c[3],
+ char_wd.c[0], char_wd.c[1],
+ char_ht.c[0], char_ht.c[1],
+ delta_x.c[0], delta_x.c[1]);
+end
+
+@ This procedure transfers a character in raster form.
+This is used for characters which are too large to be stored in
+the font memory of the printer.
+@^system dependencies@>
+
+@p procedure raster_char(p:integer);
+var
+i,j,n:short;
+@!pixel_ht,@!pixel_wd,@!x_offset,@!y_offset:short;
+@!raster_address:integer;
+
+begin
+ seek(pxl_file[cur_font],pxl_dptr[cur_font]+p*4+2);
+ get(pxl_file[cur_font]);
+ pixel_wd:=pxl_file[cur_font]^.i0;
+ pixel_ht:=pxl_file[cur_font]^.i1;
+ get(pxl_file[cur_font]);
+ x_offset:=pxl_file[cur_font]^.i0;
+ y_offset:=pxl_file[cur_font]^.i1;
+ get(pxl_file[cur_font]);
+ raster_address:=pxl_file[cur_font]^.i;
+ hh:=hh-x_offset; vv:=vv-y_offset;
+ update_pos;
+ write_lj(@=#27'*r1A'@>);
+ seek(pxl_file[cur_font],raster_address+2);
+ for j:=1 to pixel_ht do begin
+ write_lj(#27'*b',(pixel_wd-1) div 8 + 1:1, 'W');
+ for i:=1 to (pixel_wd-1) div 8 + 1 do begin
+ if (i-1) mod 4 = 0 then
+ get(pxl_file[cur_font]);
+ write_lj(pxl_file[cur_font]^.c[(i-1) mod 4]);
+ end;
+ end;
+ lj_v:=lj_v+pixel_ht;
+ hh:=hh+char_pixel_width(cur_font)(p); vv:=vv+y_offset;
+ write_lj(@=#27'*rB'@>);
+end;
+
+@* Optional modes of output.
+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,
+\.{DVIplus} 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.
+
+Another option is the page offset. That is the point on the physical
+printer page where the point (0,0) in the DVI file is mapped.
+
+Normally \.{DVIplus} uses default values for the options.
+It can be started in such a way that it engages
+the user in a brief dialog so that the
+options will be specified.
+@^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}
+@!copies:integer;
+@!h_offset,@!v_offset:integer; {offset where to put (0,0) on physical page}
+
+@ 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:=100; start_vals:=0; start_there[0]:=false; copies:=1;
+resolution:=300.0; new_mag:=0;
+h_offset:=210; v_offset:=100;
+
+@ 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}
+@!arg_index:short; {which command line argument is being processed}
+@!interactive:boolean;
+
+@ 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 call a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+@^system dependencies@>
+
+@d update_terminal == prompt(term_out) {empty the terminal output buffer}
+
+@ During the dialog, \.{DVIplus} 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;
+k:=0;
+if eoln(term_in) then read_ln(term_in)
+else begin while (k<terminal_line_length)and not eoln(term_in) do
+ begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
+ end;
+ read_ln(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 \.{DVIplus} begins.
+@^system dependencies@>
+
+@p procedure dialog;
+label 2,3,6,7,8,9,99;
+var i,j,k:short;
+begin rewrite(term_out, '1', 'NOCCTL'); {prepare the terminal for output}
+reset(term_in, '1'); {and for input}
+print_ln(banner);
+@<Get flags, check if interactive@>;
+@<Determine the desired |start_count| values@>;
+@<Determine the desired |max_pages|@>;
+@<Determine the number of copies@>;
+@<Determine the page offset@>;
+@<Print all the selected options@>;
+end;
+
+@ @<Get flags, check if interactive@>=
+arg_index:=1; interactive:=false;
+repeat
+ i:=parameters(arg_index,cur_name,name_length);
+ if cur_name[1]='-' then begin
+ incr(arg_index);
+ if i=1 then
+ interactive:=true
+ else case cur_name[2] of
+ 'i','I':interactive:=true;
+ othercases begin end;
+ endcases;
+ end;
+until cur_name[1]<>'-';
+if not interactive then goto 99;
+
+@ @<Determine the desired |start...@>=
+2: print('Starting page (default=*): ');
+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 print('Type, e.g., 1.*.-5 to specify the ');
+ print_ln('first page with \count0=1, \count2=-5.');
+ goto 2;
+ end;
+ until start_vals=k
+
+@ @<Determine the desired |max_pages|@>=
+3: print('Maximum number of pages (default=100): ');
+input_ln; buf_ptr:=0;
+if buffer[0]<>" " then
+ begin max_pages:=get_integer;
+ if max_pages<=0 then
+ begin print_ln('Please type a positive number.');
+ goto 3;
+ end;
+ end
+
+@ @<Determine the number of copies@>=
+6: print('Number of copies (default=1): ');
+input_ln; buf_ptr:=0;
+if buffer[0]<>" " then
+ if (buffer[0]>="0")and(buffer[0]<="9") then copies:=get_integer
+ else begin print('Type a positive integer to specify ');
+ print_ln('the number of copies.');
+ goto 6;
+ end
+
+@ @<Determine the page offset@>=
+7: print('Page offset in dots (default=210,100): ');
+input_ln; buf_ptr:=0;
+if buffer[0]=" " then goto 9;
+if ((buffer[0]>="0")and(buffer[0]<="9")) or (buffer[0]="-") then
+ h_offset:=get_integer
+else goto 8;
+if (buffer[buf_ptr]=",") then incr(buf_ptr);
+if (buffer[buf_ptr]=" ") then incr(buf_ptr);
+if ((buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9")) or
+ (buffer[buf_ptr]="-") then begin
+ v_offset:=get_integer;
+ goto 9; end
+else goto 8;
+8:print('Specify a dot coordinate pair where the (0,0) ');
+ print_ln('point will be placed.');
+ goto 7;
+9:
+
+@ After the dialog is over, we print the options so that the user
+can see what \.{DVIplus} thought was specified.
+
+@<Print all the selected options@>=
+99: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_nl;
+ end;
+print_ln(' Maximum number of pages = ',max_pages:1);
+print_ln(' Page offset = (', h_offset:1, ',', v_offset:1, ')');
+
+@* Defining fonts.
+\.{DVIplus} reads the postamble first and loads
+all of the fonts defined there; then it processes the pages.
+
+@ Approximate the desired magnification to an available one.
+The ``magig numbers'' that the desired magnification
+is compared to are calculated as
+$1500*1.2^m$, where $m = 1/4,3/4,1.5,2.5,3.5,4.5$
+
+@p function approx_mag(f:integer;d_mag:integer):integer;
+
+begin
+ if d_mag < 1569 then
+ approx_mag := 1500
+ else if d_mag < 1720 then
+ approx_mag := 1643
+ else if d_mag < 1971 then
+ approx_mag := 1800
+ else if d_mag < 2366 then
+ approx_mag := 2160
+ else if d_mag < 2839 then
+ approx_mag := 2592
+ else if d_mag < 3407 then
+ approx_mag := 3110
+ else if d_mag < 4089 then
+ approx_mag := 3732
+ else
+ approx_mag:= 4479;
+end;
+
+@ The following subroutine does the necessary things when a \\{fnt\_def}
+command is being processed.
+
+@p procedure define_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}
+@!m:integer; {|mag| corrected for 300 pixels/inch}
+@!r:0..name_length; {index into |cur_name|}
+@!j,@!k:0..name_size; {indices into |names|}
+@!mismatch:boolean; {do names disagree?}
+begin if nf=max_fonts then abort('DVIplus capacity exceeded (max fonts=',
+ max_fonts:1,')!');
+@.DVIplus 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|, and
+ print the font name@>;
+ if f<nf then print_ln('---this font was already defined!');
+@.this font was already defined@>
+ @<Load the new font, unless there are problems@>
+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; n:=get_byte;
+if font_name[nf]+n+p>name_size then
+ abort('DVIplus capacity exceeded (name size=',name_size:1,')');
+@.DVIplus capacity exceeded...@>
+font_name[nf+1]:=font_name[nf]+n+p;
+if n+p=0 then abort('Null font name')
+@.Null font name@>
+else for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=get_byte;
+font_used_on[nf+1]:=0;
+incr(nf);
+if f=nf-1 then begin
+ print('Font ', e:1, ': '); print_font(nf-1);
+ update_terminal;
+end;
+decr(nf)
+
+@ @<Load the new font, unless there are problems@>=
+begin
+@<Compute |desired_mag|@>;
+@<Move font name into the |cur_name| string@>;
+if not open_pxl_file(nf) then begin
+ print_nl;
+ abort('Cannot open PXL file ', cur_name); end
+@.Cannot open PXL file@>
+else begin if (q<=0)or(q>=@'1000000000) then begin
+ print_nl;
+ abort('PXL file not loaded, bad scale (',q:1,')!'); end
+@.bad scale@>
+ else if (d<=0)or(d>=@'1000000000) then begin
+ print_nl;
+ abort('PXL file not loaded, bad design size (',d:1,')!'); end
+@.bad design size@>
+ else begin
+ in_PXL(q);
+ @<Finish loading the new font info@>;
+ end
+end
+end
+
+@ @<Compute |desired_mag|@>=
+desired_mag:=round((mag * q * (300.0/200.0))/d+0.5);
+
+@ @<Finish loading...@>=
+begin font_space[nf]:=q div 6; {this is a 3-unit ``thin space''}
+if (c<>0)and(pxl_check_sum<>0)and(c<>pxl_check_sum) then
+ begin print(' ---beware: check sums do not agree!');
+@.beware: check sums do not agree@>
+@.check sums do not agree@>
+ print(' (',c:1,' vs. ',pxl_check_sum:1,')');
+ end;
+d:=round((100.0*conv*q)/(true_conv*d));
+if d<>100 then
+ print(' (magnified ',d:1,'%)');
+@.this font is magnified@>
+incr(nf); {now the new font is officially present}
+font_space[nf]:=0; {for |out_space| and |out_vmove|}
+end
+
+@ If |p=0|, i.e., if no font directory has been specified, \.{DVIplus}
+uses the default font directory, which is a
+system-dependent place where the standard fonts are kept.
+
+In RTE--A, the \.{PXL} files are kept in directories called
+\.{/TeX/Fonts/MagXXXX}, where \.{XXXX} is the magnification.
+The string variable |default_prefix| contains the prefix of these names.
+@^system dependencies@>
+
+@d default_prefix_name=='/TeX/Fonts/Mag'
+@d default_prefix_length=14 {change this to the correct length}
+
+@<Glob...@>=
+@!default_prefix:packed array[1..default_prefix_length] of char;
+
+@ @<Set init...@>=
+default_prefix:=default_prefix_name;
+
+@ The string |cur_name| is set to the external name of the
+\.{PXL} file for the current font and magnification.
+@^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_prefix_length do
+ cur_name[k]:=default_prefix[k];
+ r:=default_prefix_length;
+ incr(r);
+ best_mag:=approx_mag(font_name[nf],desired_mag);
+ m:=best_mag;
+ cur_name[r]:=xchr[ m div 1000 + xord['0']];
+ incr(r);
+ cur_name[r]:=xchr[ (m div 100) mod 10 + xord['0']];
+ incr(r);
+ cur_name[r]:=xchr[ (m div 10) mod 10 + xord['0']];
+ incr(r);
+ cur_name[r]:=xchr[ m mod 10 + xord['0']];
+ incr(r);
+ cur_name[r]:='/'; end
+else
+ r:=0;
+
+for k:=font_name[nf] to font_name[nf+1]-1 do
+ begin incr(r);
+ if r+4>name_length then
+ abort('DVIplus capacity exceeded (max font name length=',
+ name_length:1,')!');
+@.DVIplus capacity exceeded...@>
+ cur_name[r]:=xchr[names[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='P'; cur_name[r+3]:='X'; cur_name[r+4]:='L'
+
+@* Interpreting the {\tentex DVI} file.
+The main work of \.{DVIplus} 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)|.
+The |lj_h| and |lj_v| registers hold the current actual cursor position
+of the printer.
+
+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,@!vv:integer; {current state values}
+@!lj_h,@!lj_v:integer; {current cursor position}
+@!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}
+
+@ Three characteristics of the pages (their |max_v|, |max_h|, and
+|max_s|) are specified in the postamble, and a warning message
+is printed if these limits are exceeded. Actually |max_v| is set to
+the maximum height plus depth of a page, and |max_h| to the maximum width,
+for purposes of page layout. Since characters can legally be set outside
+of the page boundaries, it is not an error when |max_v| or |max_h| is
+exceeded. But |max_s| should not be exceeded.
+
+The postamble also specifies the total number of pages; \.{DVIplus}
+checks to see if this total is accurate.
+
+@<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_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels}
+@!total_pages:integer; {the stated total number of pages}
+@!page_count:integer; {the total number of pages seen so far}
+
+@ @<Set init...@>=
+max_v:=@'17777777777-99; max_h:=@'17777777777-99; max_s:=stack_size+1;@/
+max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=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.
+
+@d four_cases(#)==#,#+1,#+2,#+3
+@d eight_cases(#)==four_cases(#),four_cases(#+4)
+@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
+@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
+@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)
+
+@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;
+
+@ Strictly speaking, the |do_page| procedure is really a function with
+side effects, not a `\&{procedure}'; it returns the value |false| if
+\.{DVIplus} should be aborted because of some unusual happening. The
+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=46 {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}
+@!ss:integer; {stack size to print}
+@!cur_font:integer; {current internal font number}
+@!prev_font:integer;
+@!fonts_in_use:integer; {how many are currently loaded}
+@!fonts_on_page:integer; {how many fonts used on current page}
+
+@ @<Set init...@>=
+fonts_in_use:=0;
+
+@ Here is the overall setup.
+
+@p @t\4@>@<Declare the function called |special_cases|@>@;
+procedure do_page;
+label fin_set,fin_rule,move_right,done,exit;
+var o:eight_bits; {operation code of the current command}
+@!p,@!q:integer; {parameters of the current command}
+pp,qq:integer;
+i,j,k:integer;
+@!a:integer; {byte number of the current command}
+@!hhh:integer; {|h|, rounded to the nearest pixel}
+@!height,pitch:i2c;
+begin cur_font:=nf; {set current font undefined}
+prev_font:=-1;
+s:=0; h:=round(h_offset/conv); v:=round(v_offset/conv);
+w:=0; x:=0; y:=0; z:=0; hh:=pixel_round(h); vv:=pixel_round(v);
+lj_h:=-10000; lj_v:=-10000;
+ {initialize the state variables}
+while true do @<Translate the next command in the \.{DVI} file@>;
+exit:
+end;
+
+@
+
+@d error(#)==print_ln('% ',#)
+
+@<Translate the next command...@>=
+begin a:=cur_loc;
+o:=get_byte; p:=first_par(o);
+if eof_dvi_file then bad_dvi('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 @<Translate a |set_char| command@>
+else case o of
+ four_cases(set1),
+ four_cases(put1): bad_dvi('illegal character (', p:1, ')');
+ set_rule,
+ put_rule: goto fin_rule;
+ @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
+ @t\4@>@<Cases for horizontal motion@>@;
+ othercases if special_cases(o,p,a) then goto done
+ else bad_dvi(' ')
+ endcases
+
+@ @<Declare the function called |special_cases|@>=
+function special_cases(@!o:eight_bits;@!p,@!a:integer):boolean;
+label change_font,move_down,done,9998;
+var q:integer; {parameter of the current command}
+q1,q2:integer; {dummies for |fnt_def|}
+@!k:integer; {loop index}
+@!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
+@!pure:boolean; {is the command error-free?}
+@!vvv:integer; {|v|, rounded to the nearest pixel}
+begin pure:=true;
+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: begin error('preamble command within a page!'); goto 9998;
+ end;
+@.preamble command within a page@>
+post,post_post: begin error('postamble command within a page!'); goto 9998;
+@.postamble command within a page@>
+ end;
+othercases begin error('undefined command ',o:1,'!');
+ goto done;
+@.undefined command@>
+ end
+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|@>;
+9998: pure:=false;
+done: special_cases:=pure;
+end;
+
+@ @<Cases for commands |nop|, |bop|, \dots, |pop|@>=
+nop: goto done;
+bop: begin error('bop occurred before eop'); bad_dvi(' ');
+@.bop occurred before eop@>
+ end;
+eop: begin
+ write_lj(#12);
+ if s<>0 then error('stack not empty at end of page (level ',
+ s:1,')!');
+@.stack not empty...@>
+ return;
+ end;
+push: begin
+ if s=max_s_so_far then
+ begin max_s_so_far:=s+1;
+ if s=max_s then error('deeper than claimed in postamble!');
+@.deeper than claimed...@>
+@.push deeper than claimed...@>
+ if s=stack_size then
+ abort('DVIplus 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); ss:=s-1; goto done;
+ end;
+pop: begin
+ if s=0 then error('(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];
+ end;
+ ss:=s; 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==begin
+ 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;
+end
+
+@<Cases for horizontal motion@>=
+four_cases(right1):out_space;
+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==begin
+ if abs(p)>=5*font_space[cur_font] then vv:=pixel_round(v+p)
+ else vv:=vv+pixel_round(p);
+ goto move_down;
+end
+
+@<Cases for vertical motion@>=
+four_cases(down1):out_vmove;
+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),
+four_cases(fnt1):
+ goto change_font;
+four_cases(fnt_def1): begin
+ @<Skip a |fnt_def| command@>;
+ goto done;
+ end;
+
+@ @<Skip a |fnt_def| command@>=
+q:=signed_quad; q:=signed_quad; q:=signed_quad;
+q1:=get_byte; q2:=get_byte;
+for k:=1 to q1+q2 do q:=get_byte;
+
+@ @<Translate an |xxx| command and |goto done|@>=
+begin bad_char:=false;
+for k:=1 to p do
+ begin q:=get_byte;
+ if not ((q>=" ")and(q<="~")) then
+ bad_char:=true;
+ end;
+if bad_char then error('non-ASCII character in xxx command!');
+@.non-ASCII character...@>
+goto done;
+end
+
+@ @<Translate a |set_char|...@>=
+goto fin_set
+
+@ This is the code that checks whether the next character to be
+printed in |cur_font| has occurred previously.
+If not, the character data is downloaded to the printer. If the character
+is too large, the data is sent as a raster image.
+If |cur_font| is also new, the font header is first downloaded.
+The number of fonts stored in the printer is kept at |max_printer_fonts|
+maximum. When more fonts are needed, the least recently used one is
+deleted.
+
+The value of |max_printer_fonts| was choosed by waving a magig rod;
+it would be more appropriate to calculate exactly how many bytes of
+the user-available memory in the LaserJet+ is in use, and base the
+decisions whether we must delete some fonts on that.
+(Why can't you ask the printer how much memory it has left?).
+
+There should also be a test whether the number of fonts on a page
+exceeds the maximum 16.
+
+@<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>127) then q:=invalid_width
+else q:=char_width(cur_font)(p);
+if q=invalid_width then
+ begin error('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;
+if font_used_on[cur_font]=0 then begin
+ if fonts_in_use=max_printer_fonts then
+ @<Delete least recently used font@>;
+ incr(fonts_on_page);
+ @<Download font header@> end
+else if font_used_on[cur_font]<page_count then
+ incr(fonts_on_page);
+if fonts_on_page>max_fonts_on_page then
+ error('too many fonts on this page');
+font_used_on[cur_font]:=page_count;
+if cur_font <> prev_font then
+ write_lj(@=#27'('@>,font_num[cur_font]:1,'X');
+prev_font:=cur_font;
+if char_status(cur_font)(p) = too_large then
+ raster_char(p)
+else begin
+ if char_status(cur_font)(p) = not_loaded then
+ download_char(p);
+ if char_status(cur_font)(p) > 0 then begin
+ vv:=vv+char_status(cur_font)(p);
+ update_pos;
+ write_lj(vis_chr(p));
+ vv:=vv-char_status(cur_font)(p); end
+ else begin
+ update_pos;
+ write_lj(vis_chr(p));
+ end;
+ lj_h:=lj_h+char_pixel_width(cur_font)(p);
+end;
+if o>=put1 then begin
+ hh:=hh-char_pixel_width(cur_font)(p);
+ goto done;
+end;
+if q=invalid_width then q:=0
+else hh:=hh+char_pixel_width(cur_font)(p);
+goto move_right
+
+@ @<Download font header@>=
+begin
+ height.i0:=4*round(font_design_size[cur_font]*
+ font_mag[cur_font]/1000.0*conv);
+ if (height.i0 < 0) or (height.i0>10922) then
+ height.i0:= 10922;
+ pitch.i0:=height.i0-20; {is this value used for anything in the printer??}
+
+ write_lj(@=#27'*c'@>,font_num[cur_font]:1,
+ {char cell 255*255 pixels max}
+ {baseline distance set to $255-$|baseline| }
+ @='D'#27')s26W'#0#26#0#1#0#0#0@>, chr(255-baseline),
+ @=#0#255#0#255#0#1#1#21@>,
+ pitch.c[0], pitch.c[1], height.c[0], height.c[1],
+ @=#0#0#0#0#0#0#27'*c4F'@>);
+ incr(fonts_in_use);
+end
+
+@ @<Delete least recently...@>=
+begin
+ j:=9999;
+ k:=nf;
+ for i:=0 to nf-1 do
+ if font_used_on[i] < j then begin
+ j:=font_used_on[i];
+ k:=i;
+ end;
+ write_lj(@=#27'*c'@>,font_num[k]:1,'d2F');
+ font_used_on[k]:=0;
+ for i:=0 to 127 do
+ if (char_status(k)(i) > 0) or (char_status(k)(i) = loaded_ok) then
+ char_status(k)(i):=not_loaded;
+ decr(fonts_in_use);
+end
+
+@ @<Finish a command that either sets or puts a rule...@>=
+q:=signed_quad;
+qq:=rule_pixels(q);
+pp:=rule_pixels(p);
+if (p>0) and (q>0) then begin
+ vv:=vv-pp;
+ update_pos;
+ write_lj(@=#27'*c'@>,qq:1,'a',pp:1,'b0P');
+ vv:=vv+pp;
+end;
+if o=put_rule then goto done;
+hh:=hh+qq;
+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. \.{DVIplus} ensures that the
+amount of drift will never exceed |max_drift| pixels.
+
+Since \.{DVIplus} is intended to diagnose strange errors, it checks
+carefully to make sure that |h| and |v| do not get out of range.
+Normal \.{DVI}-reading programs need not do this.
+
+@d infinity==@'17777777777 {$\infty$ (approximately)}
+@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 error('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 error('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;
+end;
+h:=h+q;
+if abs(h)>max_h_so_far then
+ begin if abs(h)-round(h_offset/conv)>max_h+99 then
+ begin error('warning: h > ',max_h:1,'!');
+@.warning: |h|...@>
+ max_h:=abs(h);
+ end;
+ max_h_so_far:=abs(h);
+ end;
+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 error('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 error('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;
+end;
+v:=v+p;
+if abs(v)>max_v_so_far then
+ begin if abs(v)-round(v_offset/conv)>max_v+99 then
+ begin error('warning: v > ',max_v:1,'!');
+@.warning: |v|...@>
+ max_v:=abs(v);
+ end;
+ max_v_so_far:=abs(v);
+ end;
+goto done
+
+@ @<Finish a command that changes the current font...@>=
+begin
+ font_num[nf]:=p; cur_font:=0;
+ while font_num[cur_font]<>p do incr(cur_font);
+ goto done
+end
+
+@* Using the backpointers.
+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<>dvi_id 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 another one that we will need shortly.
+
+@<Glob...@>=
+@!post_loc:integer; {byte location where the postamble begins}
+@!first_backpointer:integer; {the pointer following |post|}
+@!start_loc:integer; {byte location of the first page to process}
+@!start_inx:integer; {index into |page_start| for first page}
+@!last_loc:integer; {byte localtion of last page to process}
+@!page_start:array[1..max_bops] of integer; {pointers to |bop|s}
+
+@ The next routines follow the backpointers
+to move through a \.{DVI} file in reverse order. \.{DVIplus} does this because
+it wants to print the pages backwards as the LaserJet stacks
+them with the printed side up.
+
+First we search for the starting page and the last page.
+
+@<Scan for page range to print@>=
+q:=post_loc; p:=first_backpointer; start_loc:=-1;
+repeat
+ {now |q| points to a |post| or |bop| command; |p>=0| is prev pointer}
+ if p>q-46 then
+ bad_dvi('page link ',p:1,' after byte ',q:1);
+@.page link wrong...@>
+ q:=p; move_to_byte(q);
+ k:=get_byte;
+ if k=bop then incr(page_count)
+ else bad_dvi('byte ',q:1,' is not bop');
+@.byte n is not bop@>
+ if page_count>max_bops then bad_dvi('there are too many pages');
+@.there are too many pages@>
+ page_start[page_count]:=q;
+ for k:=0 to 9 do count[k]:=signed_quad;
+ if start_match then begin start_loc:=q; start_inx:=page_count; end;
+ p:=signed_quad;
+until p<0;
+if start_loc<0 then abort('starting page number could not be found!');
+@.starting page number...@>
+if (start_inx > max_pages) then
+ last_loc:=page_start[start_inx-max_pages+1]
+else
+ last_loc:=page_start[1];
+if page_count<>total_pages then
+ print_ln('there are really ',page_count:1,
+ ' pages, not ',total_pages:1,'!');
+@.there are really n pages@>
+
+@ This is the code that really goes through the pages to be printed
+(in reverse order). It starts from the page pointed to by |last_loc| and
+proceeds up to |start_loc|.
+
+The code shown here uses a convention that has proved to be useful:
+If the starting page was specified as, e.g., `\.{1.*.-5}', then
+all page numbers in the file are displayed by showing the values of
+counts 0, 1, and~2, separated by dots. Such numbers can, for example,
+be displayed on the console of a printer when it is working on that
+page.
+
+@<Translate the page range in reverse order@>=
+page_count:=0;
+q:=last_loc;
+repeat
+ move_to_byte(q); k:=get_byte;
+ incr(page_count);
+ fonts_on_page:=0;
+ for k:=0 to 9 do count[k]:=signed_quad;
+ q:=signed_quad;
+ print('[');
+ for k:=0 to start_vals do
+ begin print(count[k]:1);
+ if k<start_vals then print('.');
+ end;
+ update_terminal;
+ do_page;
+ print('] ');
+ update_terminal;
+until q<start_loc;
+
+@* 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 \.{DVIplus} is called upon to read, translate,
+and check the rest of the postamble.
+
+@p procedure read_postamble;
+var k:integer; {loop index}
+@!p,@!q,@!m:integer; {general purpose registers}
+begin post_loc:=cur_loc-5;
+@.Postamble starts at byte n@>
+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;@/
+if total_pages>max_bops then
+ bad_dvi('enormous number of pages (', total_pages:1, ')');
+@.enormous number of pages@>
+@<Process the font definitions of the postamble@>;
+@<Make sure that the end of the file is well-formed@>;
+end;
+
+@ No warning is given when |max_h_so_far| exceeds |max_h| by less than~100,
+since 100 units is invisibly small; it's approximately the wavelength of
+visible light, in the case of \TeX\ output. Rounding errors can be expected
+to make |h| and |v| slightly more than |max_h| and |max_v|, every once in
+a~while; hence small discrepancies are not cause for alarm.
+
+@ When we get to the present code, the |post_post| command has
+just been read.
+
+@<Make sure that the end of the file is well-formed@>=
+q:=signed_quad;
+if q<>post_loc then
+ print_ln('bad postamble pointer in byte ',cur_loc-4:1,'!');
+@.bad postamble pointer@>
+m:=get_byte;
+if m<>dvi_id then print_ln('identification in byte ',cur_loc-1:1,
+@.identification...should be n@>
+ ' should be ',dvi_id:1,'!');
+k:=cur_loc; m:=223;
+while (m=223)and not eof_dvi_file do m:=get_byte;
+if not eof_dvi_file then bad_dvi('signature in byte ',cur_loc-1:1,
+@.signature...should be...@>
+ ' should be 223')
+else if cur_loc<k+4 then
+ print_ln('not enough signature bytes at end of file (',
+@.not enough signature bytes...@>
+ cur_loc-k:1,')');
+
+@ @<Process the font definitions...@>=
+repeat k:=get_byte;
+if (k>=fnt_def1)and(k<fnt_def1+4) then
+ begin p:=first_par(k); define_font(p); print_nl; 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@>
+
+@* The main program.
+Now we are ready to put it all together. This is where \.{DVIplus} starts,
+and where it ends.
+
+@p begin dont_catch_errors; initialize; {get all variables initialized}
+dialog; {set up all the options}
+@<Process the preamble@>;
+@<Find the postamble, working back from the end@>;
+read_postamble;
+print_ln('Total of ', nf:1, ' fonts.');
+@<Scan for page range to print@>;
+write_lj(@=#27'E'#27'&l'@>, copies:1, @='X'#27'*t300R'@>);
+@<Translate the page range...@>;
+write_lj(@=#27'&l1X'@>);
+write_ln(laser_file,'_');
+print_nl
+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}
+
+@ A \.{DVI}-reading program that reads the postamble first need not look at the
+preamble; but \.{DVIplus} looks at the preamble in order to do error
+checking, and to display the introductory comment.
+
+@<Process the preamble@>=
+if not open_dvi_file then
+ bad_dvi('cannot open file');
+@<Open and lock |laser_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<>dvi_id then
+ print_ln('identification in byte 1 should be ',dvi_id:1,'!');
+@.identification...should be n@>
+@<Compute the conversion factor@>;
+p:=get_byte; {fetch the length of the introductory comment}
+print('''');
+while p>0 do
+ begin decr(p); print(xchr[get_byte]);
+ end;
+print_ln('''')
+
+@ 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 sho
diff --git a/dviware/laserjet/readme b/dviware/laserjet/readme
new file mode 100644
index 0000000000..7b9d18416b
--- /dev/null
+++ b/dviware/laserjet/readme
@@ -0,0 +1,22 @@
+This file is presented here just as it was received from
+Finland. There is a considerable amount of system-dependent
+code in it which would be better in a change file. Also,
+it runs the LaserJet+ directly, rather than through the
+creation of an intermediate file. But it is said to work,
+and that is better than we had before.
+
+It is bound to need substantial revision before it will
+work under 4.2/3 BSD Unix. Ideally, this work should
+involve the elimination of the obvious system dependencies from
+the web file, and the creation of a proper change file.
+
+Also, it is probably preferable to create the usual sort of
+intermediate file, rather than to attempt to run the LaserJet
+as an online device.
+
+If you are in the mood to make the change from PXL to GF
+fonts, please do. And let us know about it when you are
+satisfied with the results.
+
+Best of luck.
+