diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /dviware/vutex |
Initial commit
Diffstat (limited to 'dviware/vutex')
-rw-r--r-- | dviware/vutex/cp6.chg | 715 | ||||
-rw-r--r-- | dviware/vutex/cp6.hlp | 115 | ||||
-rw-r--r-- | dviware/vutex/cp6.ins | 18 | ||||
-rw-r--r-- | dviware/vutex/nonascii.fnt | 26 | ||||
-rw-r--r-- | dviware/vutex/test.tex | 68 | ||||
-rw-r--r-- | dviware/vutex/vaxvms.chg | 632 | ||||
-rw-r--r-- | dviware/vutex/vaxvms.hlp | 129 | ||||
-rw-r--r-- | dviware/vutex/vaxvms.ins | 20 | ||||
-rw-r--r-- | dviware/vutex/vutex.web | 3533 |
9 files changed, 5256 insertions, 0 deletions
diff --git a/dviware/vutex/cp6.chg b/dviware/vutex/cp6.chg new file mode 100644 index 0000000000..b5f8367961 --- /dev/null +++ b/dviware/vutex/cp6.chg @@ -0,0 +1,715 @@ + CP6 changes file for the ASCII driver written by Warren Wolfe, Dec 10, 1987 + Copyright 1987 CUBE Software, Victoria, B.C., Canada + +@x [2] +@d banner=='This is ',clone,', Version 1.00' +@y +@d banner=='This is ',clone,', DPS8/CP6 Version 1.00' +@z + +@x [3] +@d othercases == others: {default for cases not listed explicitly} +@y +@d othercases == otherwise {CP6 default for cases not listed explicitly} +@z + +@x [4] add tfm_file,gen_input,and input to program header +@p program vutex(dvi_file,bit_file,input,output); +label @<Labels in the outer block@>@/ +const @<Constants in the outer block@>@/ +type @<Types in the outer block@>@/ +var @<Globals in the outer block@>@/ +procedure initialize; {this procedure gets things started properly} + var i:integer; {loop index for initializations} + begin print_ln(banner);@/ + @<Set initial values@>@/ + end; +@y +@p program vutex(dvi_file,bit_file,tfm_file,gen_input,input,output); +label @<Labels in the outer block@>@/ +const @<Constants in the outer block@>@/ +type @<Types in the outer block@>@/ +var @<Globals in the outer block@>@/ +@<DPS8/CP-6 procedures@>@/ +procedure initialize; {this procedure gets things started properly} + var i:integer; {loop index for initializations} + begin open_output ; { must explicitly open output } + print_ln(banner);@/ + @<Set initial values@>@/ + end; +@z + +@x [9] +@!ASCII_code=" ".."~"; {a subrange of the integers} +@y +@!ASCII_code=0.."~"; {a subrange of the integers} +@z + +@x [10] +@!text_file=packed file of text_char; +@y +@!text_file=text; +@z + +@x [30] +@!byte_file=packed file of eight_bits; {files that contain binary data} +@!word_file=packed file of integer; {for pixel file words} +@y +{later we'll define files that contain binary data} +@z + +@x [31] fix defn of dvi_file and pxl_file, add tfm_file,bit_file +@<Glob...@>= +@!dvi_file:byte_file; {the stuff we are \.{DVI}typing} +@!tfm_file:byte_file; {a font metric file} +@y +@<Glob...@>= +@!dvi_file:packed file of block; {the stuff we are \.{DVI}typing} +@!tfm_file:packed file of block; {a font metric file} +@!dvi_count:integer; {number of bytes read from current block of |dvi_file|} +@!tfm_count:integer; {number of bytes read from current block of |tfm_file|} +@!eof_tfm:boolean; { true when end of \.{TFM} file reached } +@z + +@x [32] fix up opens and closes +@ To prepare these files for input, we |reset| them. An extension of +\PASCAL\ is needed in the case of |tfm_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. If |eof(f)| is true immediately after +|reset(f,s)| has acted, we assume that no file named |s| is accessible. +@^system dependencies@> + +@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|} +begin reset(dvi_file); +cur_loc:=0; +end; +@# +procedure reopen_dvi_file; {reopens the |dvi_file| for the next scan} +begin reset(dvi_file); +cur_loc:=0; +end; +@# +procedure open_bit_file; {prepares final output for writing} +begin rewrite(bit_file); +bit_is_open := true ; +end; +@# +procedure open_tfm_file; {opens \.{TFM} file} +begin reset(tfm_file,cur_name); +eof_tfm := eof(tfm_file); +end; +@# +procedure open_input_text ; {prepares to read from general purpose input} +begin reset(gen_input, cur_name) ; +end ; +@y +@ To prepare these files for input, we |reset| them. An extension of \PASCAL\ is +needed in the case of |tfm_file|, since we want to associate it with external +files whose names are specified dynamically (i.e., not known at compile time). +On CP6, we use the |set_file_parameter| extension to name the file before it is +|reset|. We also turn on FSFA for better performance. This means output files +must be explicitly closed. If |eof(f)| is true immediately after |reset(f)| has +acted, we assume that no file named |s| is accessible. +@^system dependencies@> + +@d close_file == @= close_file@> +@d close_bit_file == close_file(bit_file) +@d close_tfm_file == close_file(tfm_file) +@d close_input_text == close_file(gen_input) +@d set_file_parameters== @= set_file_parameters @> +@d file_status == @= file_status @> + +@p procedure open_output; @= {explicitly opens output to assign the dcb to #4}@> +begin close_file(output) ; +set_file_parameters(output,' ',@='dcb=#4'@>) ; +rewrite(output) ; end ; + +procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|} +begin set_file_parameters(dvi_file,' ',@='error=continue,fsfa=no,dcb=#1'@>); +reset(dvi_file); if file_status(dvi_file)>0 then abort('Cannot open dvi file!'); +dvi_count := 0; cur_loc := 0; +end; +@# +procedure reopen_dvi_file; {reopens the |dvi_file| for the next scan} +begin reset(dvi_file); dvi_count := 0; +cur_loc:=0; +end; +@# +procedure open_bit_file; {prepares final output for writing} +begin set_file_parameters(bit_file,' ','error=continue,fsfa=yes,dcb=#3'); +rewrite(bit_file); +if file_status(bit_file)>0 then + abort('Cannot open bit file or printer output!') ; +bit_is_open := true ; +end; +@# +procedure open_tfm_file; {opens \.{TFM} file} +begin set_file_parameters(tfm_file,cur_name,'error=continue,fsfa=yes,scrub=yes'); +reset(tfm_file); + eof_tfm := file_status(tfm_file)>0; + if eof_tfm then print_ln('Couldn''t find ',cur_name); + @.Couldn't find ...@> + tfm_count := 0 ; +end; +@# +procedure open_input_text ; {prepares to read from general purpose input} +begin + set_file_parameters(gen_input,cur_name,'error=continue,fsfa=yes,scrub=yes'); + reset(gen_input); + if file_status(gen_input) <> 0 then + print_ln('Couldn''t open ',cur_name); + @.Couldn't open ...@> +end; +@z + +@x [34] +@p function get_byte:integer; {returns the next byte, unsigned} +var b:eight_bits; +begin if eof(dvi_file) then get_byte:=0 +else begin read(dvi_file,b); incr(cur_loc); get_byte:=b; + end; +end; +@# +function signed_byte:integer; {returns the next byte, signed} +var b:eight_bits; +begin read(dvi_file,b); incr(cur_loc); +if b<128 then signed_byte:=b @+ else signed_byte:=b-256; +end; +@# +function get_two_bytes:integer; {returns the next two bytes, unsigned} +var a,@!b:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); +cur_loc:=cur_loc+2; +get_two_bytes:=a*256+b; +end; +@# +function signed_pair:integer; {returns the next two bytes, signed} +var a,@!b:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); +cur_loc:=cur_loc+2; +if a<128 then signed_pair:=a*256+b +else signed_pair:=(a-256)*256+b; +end; +@# +function get_three_bytes:integer; {returns the next three bytes, unsigned} +var a,@!b,@!c:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); +cur_loc:=cur_loc+3; +get_three_bytes:=(a*256+b)*256+c; +end; +@# +function signed_trio:integer; {returns the next three bytes, signed} +var a,@!b,@!c:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); +cur_loc:=cur_loc+3; +if a<128 then signed_trio:=(a*256+b)*256+c +else signed_trio:=((a-256)*256+b)*256+c; +end; +@# +function signed_quad:integer; {returns the next four bytes, signed} +var a,@!b,@!c,@!d:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d); +cur_loc:=cur_loc+4; +if a<128 then signed_quad:=((a*256+b)*256+c)*256+d +else signed_quad:=(((a-256)*256+b)*256+c)*256+d; +end; +@y +@d read_dvi_file(#)==begin + if dvi_count=block_size then begin + get(dvi_file); dvi_count:=0; + end; + #:=dvi_file^[dvi_count]; + incr(dvi_count); + end + +@p function get_byte:integer; {returns the next byte, unsigned} +var b:eight_bits; +begin if eof(dvi_file) then get_byte:=0 +else begin read_dvi_file(b); incr(cur_loc); get_byte:=b; + end; +end; +@# +function signed_byte:integer; {returns the next byte, signed} +var b:eight_bits; +begin read_dvi_file(b); incr(cur_loc); +if b<128 then signed_byte:=b @+ else signed_byte:=b-256; +end; +@# +function get_two_bytes:integer; {returns the next two bytes, unsigned} +var a,b:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); +cur_loc:=cur_loc+2; +get_two_bytes:=a*256+b; +end; +@# +function signed_pair:integer; {returns the next two bytes, signed} +var a,b:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); +cur_loc:=cur_loc+2; +if a<128 then signed_pair:=a*256+b +else signed_pair:=(a-256)*256+b; +end; +@# +function get_three_bytes:integer; {returns the next three bytes, unsigned} +var a,b,c:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c); +cur_loc:=cur_loc+3; +get_three_bytes:=(a*256+b)*256+c; +end; +@# +function signed_trio:integer; {returns the next three bytes, signed} +var a,b,c:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c); +cur_loc:=cur_loc+3; +if a<128 then signed_trio:=(a*256+b)*256+c +else signed_trio:=((a-256)*256+b)*256+c; +end; +@# +function get_word:integer; {returns the next four bytes, unsigned} +var a,b,c,d:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c); read_dvi_file(d); +cur_loc:=cur_loc+4; +get_word:=((a*256+b)*256+c)*256+d; +end; +@# +function signed_quad:integer; {returns the next four bytes, signed} +var a,b,c,d:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c); read_dvi_file(d); +cur_loc:=cur_loc+4; +if a<128 then signed_quad:=((a*256+b)*256+c)*256+d +else signed_quad:=(((a-256)*256+b)*256+c)*256+d; +end; +@z + +@x [37] +@ We need a function that will read in a word from the \.{TFM} file. If +the particular system +@^system dependencies@> +requires buffering, here is the place to do it. It also sets a global flag +|eof_tfm| when it reaches the end of the file. If this flag is set on +entrance to |load_tfm_file|, it is assumed that the file is bad. + +@p function tfm_integer : integer ; +var i:integer; +begin read(tfm_file, i); +eof_tfm:=eof(tfm_file); +tfm_integer:=i; +end; + +@ There is nothing wrong with defining |eof_tfm| here. + +@<Glob...@>= +@!eof_tfm:boolean; {true when end of \.{TFM} file is reached.} +@y +@ We need a function that will read in a word from the \.{TFM} file. If +the particular system +@^system dependencies@> +requires buffering, here is the place to do it. It also sets a global flag +|eof_tfm| when it reaches the end of the file. If this flag is set on +entrance to |load_tfm_file|, it is assumed that the file is bad. + +Since |tfm_file| is blocked, we need a function to read an integer +from it. + +@d read_tfm_file(#)==begin + if tfm_count=block_size then begin + get(tfm_file); tfm_count:=0; + end; + #:=tfm_file^[tfm_count]; + incr(tfm_count); + end +@# +@p function tfm_integer : integer; +var i,@!result,@!temp:integer; +begin + result := 0; + for i:=1 to 4 do begin@/ + read_tfm_file(temp); result := result*two_8+temp; + end; + eof_tfm := eof(tfm_file); + tfm_integer := result ; +end; +@z + +@x [41] +@ And, finally, we have the procedure which will extract the information from +the \.{TFM} file. This routine is for input from the \.{TFM} file, which is (or +should be) a |packed file of integer|, so this should cause no grief. See the +documentation on |tftopl| for the structure of the file. + + +@ @p procedure load_tfm_file ; {read in the width data} +label 9997, {used for bad format} + 9999; {used for normal exit} +@y +@ For input and output from the \.{TFM} file, we have this procedure to +read the \.{TFM} file into memory after the current |next_mem_free|. +See the documentation on |tftopl| for the structure of the file. + + +@ @p procedure load_tfm_file ; {read in the width data} +@z + +@x [42] + if eof_tfm then goto 9997 ; + while not eof_tfm do begin + mem[k] := tfm_integer ; + k := k + 1 ; + if k > max_mem_size then + abort(clone,' memory size exceeded on load of tfm file!') ; + end ; +@y + while not eof_tfm do begin + mem[k] := tfm_integer ; + k := k + 1 ; + if k > max_mem_size then + abort(clone,' memory size exceeded on load of tfm file!') ; + @.memory size exceeded ... tfm file!@> + end ; + close_tfm_file; + if k = next_mem_free then + print_ln('---not loaded, TFM file is bad') + @.---not loaded, TFM file is bad@> + else begin +@z + +@x [42] + goto 9999 ; +9997: print_ln('---not loaded, TFM file is bad') ; +9999: end ; +@y + end; +end; +@z + +@x [43] add CP6 routine to get options from command line +@d errors_only=0 {value of |out_mode| when minimal printing occurs} +@y +@d CP6_parse_command == @= TEX_LIB$PARSE_COMMAND@> +@d CP6_interaction_mode == @= TEX_LIB$INTERACTION_MODE@> +@d errors_only=0 {value of |out_mode| when minimal printing occurs} +@z + +@x [47] +and |term_out| for terminal output. +@^system dependencies@> + +@<Glob...@>= +@!buffer:array[0..terminal_line_length] of ASCII_code; +@!term_in:text_file; {the terminal, considered as an input file} +@!term_out:text_file; {the terminal, considered as an output file} +@y +and |term_out| for terminal output. +@^system dependencies@> + +@d term_in==input {the terminal, considered as an input file} +@d term_out==output {the terminal, considered as an output file} + +@<Glob...@>= +@!buffer: packed array[0..terminal_line_length] of ASCII_code; +@!buf_length: integer ; {length of command line} +@z + +@x [48] use break_file on CP6 +@d update_terminal == break(term_out) {empty the terminal output buffer} +@y +@d update_terminal == @= break_file@>(term_out) {empty the output buffer} +@z + +@x [49] fix input_ln procedure +begin update_terminal; reset(term_in); +if eoln(term_in) then read_ln(term_in); +k:=0; +while (k<terminal_line_length)and not eoln(term_in) do + begin buffer[k]:=xord[term_in^]; incr(k); get(term_in); + end; +buffer[k]:=" "; +@y +begin update_terminal; +k:=0; +while (k<terminal_line_length)and not eoln(term_in) do + begin buffer[k]:=xord[term_in^]; incr(k); get(term_in); + end; +buffer[k]:=" "; +read_ln(term_in); +@z + +@x [52] +@p procedure dialog; +var k:integer; {loop variable} +begin rewrite(term_out); {prepare the terminal for output} +@<Determine the desired |start_count| values@>; +@<Determine the desired |max_pages|@>; +@<Determine the truncation |print_width|@>; +@<Determine the compression mode@>; +@<Print all the selected options@>; +if compress and (print_width > (page_width * 3 div 4)) then + page_width := 4 * print_width div 3 +else if (not compress) and (print_width > page_width - hh_offset) then + page_width := print_width + hh_offset; +if page_width > max_p_width then + begin + page_width := max_p_width; + print_width := page_width - hh_offset; + end; +num_lines := total_rast div page_width; +end; +@y + +@d batch == 0 { batch mode response} +@d online == 1 { online mode response} + +@p procedure dialog; +var i, k:integer; {loop variable} +interaction: integer; { to contain response from mode query} +command_ptr: com_buf_ptr; { pointer to command line} + +begin rewrite(term_out); {prepare the terminal for output} +CP6_parse_command(command_ptr,buf_length); {get command line} +CP6_interaction_mode(interaction,batch,online); { what access mode are we?} +if (interaction=online) and (buf_length=0) then begin + @<Determine the desired |start_count| values@>; + @<Determine the desired |max_pages|@>; + @<Determine the truncation |print_width|@>; + @<Determine the compression mode@>; + @<Print all the selected options@>; +end else @<Determine the options from the command buffer@>; +if compress and (print_width > (page_width * 3 div 4)) then + page_width := 4 * print_width div 3 +else if (not compress) and (print_width > page_width - hh_offset) then + page_width := print_width + hh_offset; +if page_width > max_p_width then + begin + page_width := max_p_width; + print_width := page_width - hh_offset; + end; +num_lines := total_rast div page_width; +end; + + +@ Here are two new sections to support getting the options from the +command line. + +@<Determine the options...@>== + begin + for i := 0 to buf_length-1 do buffer[i] := xord[command_ptr^[i]]; + buffer[i+1] := "?" ; { end it with a question mark } + buf_ptr := 0 ; + repeat + case buffer[buf_ptr] of + " ",",","G","g" : ; + "S","s" : begin + @<Skip to parameter@> ; k := 0 ; + @<Parse the desired |start_vals|@> ; + end ; + "P","p" : begin + @<Skip to parameter@> ; + @<Parse the desired |max_pages|@> ; + end ; + "W","w" : begin + @<Skip to parameter@> ; + @<Parse the desired |print_width|@> ; + end ; + "T","t" : compress := false ; + "D","d" : out_mode := 1 ; + othercases + print_ln('Error on command line. Skipping: ',xchr[buffer[buf_ptr]]); + @.Error on command line...:@> + endcases ; + incr(buf_ptr); + until buf_ptr >= buf_length; + @<Print all the selected options@>; +end + +@ @<Skip to parameter@>== +while (buf_ptr < buf_length) and (buffer[buf_ptr] <> " ") and + (buffer[buf_ptr] <> "=") and (buffer[buf_ptr] <> ",") do + incr(buf_ptr); +if buffer[buf_ptr] = "=" then incr(buf_ptr); +@z + +@x [67] fix problem reading checksum from dvi file +c:=signed_quad; font_check_sum:=c;@/ +@y +c:=get_word; font_check_sum:=c;@/ +@z + +@x [68] set up directory names +@d tfm_directory_name=='TeXfonts:' +@d tfm_directory_name_length=9 +@y +@d tfm_directory_name=='.:TEX' +@d tfm_directory_name_length=5 +@z + +@x [70] fix the routine for the tfm name +@<Move tfm name into the |cur_name| string@>= +begin +for k:=1 to name_length do cur_name[k]:=' '; +r := 1 ; k := font_name ; +if names[k]=0 then + begin for i:=1 to tfm_directory_name_length do + cur_name[i]:=tfm_directory[i]; + r:=tfm_directory_name_length+1; + end else begin + while names[k] > 0 do begin + cur_name[r] := xchr[names[k]] ; + r := r + 1 ; k := k + 1 ; + end ; + end ; +k := k + 1 ; +while names[k] > 0 do begin + cur_name[r] := xchr[names[k]] ; + r := r + 1 ; k := k + 1 ; +end ; +cur_name[r] := '.' ; r := r + 1 ; +cur_name[r] := 'T' ; r := r + 1 ; +cur_name[r] := 'F' ; r := r + 1 ; +cur_name[r] := 'M' ; r := r + 1 ; +for k := 1 to r do + if (cur_name[k]>='a')and(cur_name[k]<='z') then + cur_name[k]:=xchr[xord[cur_name[k]]-@'40] ; +end ; +@y +@<Move tfm name into the |cur_name| string@>= +begin +for k:=1 to name_length do cur_name[k]:=' '; +r := 1; k := font_name + 1 ; +while names[k] > 0 do begin + cur_name[r] := xchr[names[k]] ; incr(r); incr(k); end; +cur_name[r] := ':' ; incr(r); +cur_name[r] := 't' ; incr(r); +cur_name[r] := 'f' ; incr(r); +cur_name[r] := 'm' ; incr(r); + for k := 1 to tfm_directory_name_length do + begin cur_name[r] := tfm_directory[k] ; incr(r); end ; +end; +@z + +@x [121] +First, this routine reads the file \.{nonASCII.tex.fnt} to determine which fonts +are non ASCII, i.e. not printable on the screen. This list is shorter than the +list of ASCII fonts. + +@p procedure base_font ; +var i, j, k : integer ; + f_type : integer ; {type for listed fonts} + lf,lh,bc,ec,nw,np : integer ; {help in decoding the \.{TFM} file} + value, x : integer ; {a \.{TFM} |fix_word|} +@<|base_font| variables@> +begin + @<Load non ASCII font information@> ; + @<Determine base font@> ; +end ; +@y +First, this routine reads the file \.{nonASCII\_tex\_fonts.:TEX} to determine +which fonts are non ASCII, i.e. not printable on the screen. This list is +shorter than the list of ASCII fonts. + +@p procedure base_font ; +var i, j, k : integer ; + f_type : integer ; {type for listed fonts} + lf,lh,bc,ec,nw,np : integer ; {help in decoding the /.{TFM} file} + value, x : integer ; {a /.{TFM} |fix_word|} +@!r : integer ; {local necessary for font name manipulations} +@<|base_font| variables@> +begin + @<Load non ASCII font information@> ; + @<Determine base font@> ; +end ; +@z + +@x [122] +@d font_list_file=='nonASCII.tex.fnt' {change this to correct name} +@d list_len==16 +@y +@d font_list_file=='nonASCII_tex_fonts.:TEX' +@d list_len==23 +@z + +@x [124] don't change file name to uppercase, and close input file +for k := 1 to list_len do + if (list_fonts[k] <= 'z') and (list_fonts[k] >= 'a') then + cur_name[k] := xchr[xord[list_fonts[k]]-@'40] + else cur_name[k] := list_fonts[k] ; +open_input_text ; +num_asc_fonts := 0 ; +while not eof(gen_input) do + @<Process line@> ; + +@y +for k := 1 to list_len do + cur_name[k] := list_fonts[k] ; +open_input_text ; +num_asc_fonts :=0 ; +while not eof(gen_input) do + @<Process line@> ; +close_input_text ; +@z + +@x [133] +@p procedure clean_printer ; +begin + write_ln(bit_file); + write_ln(bit_file,'The end...'); + write_ln; +end ; +@y +@p procedure clean_printer ; +begin + write_ln(bit_file); + write_ln(bit_file,'The end...'); close_bit_file ; +end ; +@z + +@x [137] +This section should be replaced, if necessary, by changes to the program +that are necessary to make \vutex\ work at a particular installation. +Any additional routines should be inserted here. +@^system dependencies@> +@y +This section should be replaced, if necessary, by changes to the program +that are necessary to make \vutex\ work at a particular installation. +Any additional routines should be inserted here. +@^system dependencies@> +Here are the remaining changes to the program +that are necessary to make \.{DVItype} work on CP6 + +@<Const...@>== +@!block_size=1024; +@!bs_minus_1=1023; +@!two_8= @'400; + +@ @<Types...@>== +@!block = packed array [0..bs_minus_1] of 0..511; +@!buf_type = packed array[0..terminal_line_length] of char; +@!com_buf_ptr = ^buf_type; {pointer to command buffer} +@!name_file_type = packed array[1..9] of char; + +@ Here are some special procedures that are external to Pascal +written in PL6 to support operating system interfacing. They +come from the |tex_lib| library supplied by Carlton University +when they distributed \TeX. + + In order to support random file positioning, we use a PL6 +routine to move the file to a particular record. This only +works on files that are blocked, in this case |dvi_file|. +@^system dependencies@> + +@f external == end + +@<DPS8/CP-6 procedures@>== +procedure CP6_parse_command(var buf_ptr: com_buf_ptr; + buf_length: integer); external; +procedure CP6_interaction_mode(var interaction:integer; + batch_mode, error_mode: integer); external; +@z + + + + + + + diff --git a/dviware/vutex/cp6.hlp b/dviware/vutex/cp6.hlp new file mode 100644 index 0000000000..324de76a58 --- /dev/null +++ b/dviware/vutex/cp6.hlp @@ -0,0 +1,115 @@ +Help File for CP6 installations: + + +syntax: +!vutex f1 [over f2] [(p1 [,p2]...)] + + +purpose: +vutex processor will print a TeX produced dvi file to an ordinary ASCII device + +f1 - name of an existing dvi file created by TeX. + +f2 - name of a valid ASCII output device. + If no file name for f2 is entered, output will default to user console + +p1,p2,... options to control output + +Options: + +The available options are given by: + S or s : for the starting page number + P or p : for the number of pages, beginning at S + W or w : for the width of the page in characters before truncation + T or t : for true TeX spacing as opposed to compressed + G or g : to initiate processing without further dialog + + +If no option is entered, you will be prompted for 4 parameters, all of which may +be defaulted by simply hitting RETURN. + +For example, + +!vutex file_dvi over LP@LASER +This is vutex, DPS8/CP6 Version 1.00 +Starting page (default=*): * +Maximum number of pages (default=1000000): *2 +Output truncated at column (default=160): *80 +Compress interword spaces, Y or N (default=Y): * +' TeX output 1989.02.28:1029' +- Page [1] [2] + +The first two are identical to those for dvilg8 and dvism, namely starting page +number and number of pages to type. The third prompt is for the page width in +characters (on most terminals, this is 80; on printers, this is 132). + +The fourth option allows the user to compress the spacing between words to one +space. The text for each line will begin in the proper column at the left +margin but all other TeX spacing between words will be reduced to one space. +If the user should not select the compress option, each separate word will be +left justified in the the space provided by TeX, and sup and super scripts will +usually appear on separate lines. This option is most useful for tables and +mathematical equations. + +An example of a command line invocation with options is given by: + +vutex file_dvi over file_vut (s=2,p=4,t) + + + +Seeing TeX output! + +One of the more frustrating things about TeX and its precise positioning of +characters is that ``what you get is far more than what you can see'' on an +ordinary ASCII terminal. The only devices for TeX output on the mainframe have +been the laser printers, and that usually equates to running to get ( and +waiting for ) your output, and excessive waste of paper. Note that at most +installations, laser output is costed at about 10 times that produced by +ordinary hammer printers. + +So, a program has been written to allow the TeX user to preview the output +before sending it to the laser printer. Of course, the terminal screen (or an +ordinary printer) still cannot handle the fine spacing of TeX, nor can it +display all the characters you might wish to print. But, you will see an +approximation to the spacing of words across the line and lines down the page. + + +If your output was to a file, such as *vut in the example, you may copy that +file to an ordinary line printer, or you may use a screen editor to view it. +Note the following: + + 1. Some characters may not appear because they landed in a place already +occupied. Two characters from a small TeX font may fit into the space provided +on the screen, so vutex can only print one such character. + + 2. The output with true TeX spacing, may be wider than an ordinary screen +of 80 characters, or even a printer page of 132 characters. With ordinary text +you can compress the text and substantially reduce the width of the page, but +text with lots of off-line text will appear best in true TeX mode. A screen +editor may be used to view the output by switching from left to right side of +the page. The present installation limits the width to 160 characters per line. + + 3. Lines of small type or with extraordinary small interline spacing may +appear superimposed in spaces in the previous line. + + 4. \special has not been implemented and will be ignored by vutex. + + 5. If characters are missing from a line because of truncation or +superimposition, then a > sign will appear in the first position of that line. + + 6. Unprintable characters from ASCII fonts, such as Greek symbols, +and special math symbols, etc. will appear as # marks. However, as much as +possible, vutex will try to print a meaningful character to suggest what is in +the TeX output e.g. S is used to replace an integral sign. + + 7. A + sign in the first position of a line means that it contains super +scripts to the next line down, while a - sign means that it contains sub +scripts to the previous line up. + + 8. At the end of each TeX page, a ruler is drawn (in centimeters) to +indicate the horizontal scale (this will not appear on the TeX output to a +printer). Usually the vertical scale is only an approximation of the true TeX +spacing, and some pages may appear physically shorter than others. If it is +desired that all pages be the same length, perhaps with a form feed, this +alteration can be made to the code. + diff --git a/dviware/vutex/cp6.ins b/dviware/vutex/cp6.ins new file mode 100644 index 0000000000..e923cfa7ca --- /dev/null +++ b/dviware/vutex/cp6.ins @@ -0,0 +1,18 @@ +To install vutex on your CP6 system: + + 1. ``weave'' vutex.web with vutex.chg to produce a printed document + + 2. check those items marked ``system dependencies'' for desired alterations + at your site. Make the necessary changes to the vutex.chg file. + For example, directory names should be changed, page_length should be + determined, and the end of page actions in the routine finish_page may be + altered to include a form feed. + + 3. ``tangle'' vutex.web with vutex.chg to produce a pascal source file + + 4. compile and link the source + + 5. alter and install the nonascii.fnt file in the desired directory. This + file lists the names of non roman tfm files with a classification code. + +That's all, folks. diff --git a/dviware/vutex/nonascii.fnt b/dviware/vutex/nonascii.fnt new file mode 100644 index 0000000000..14c8faa50e --- /dev/null +++ b/dviware/vutex/nonascii.fnt @@ -0,0 +1,26 @@ +amsy 2 +ambsy 2 +amcsc 2 +amex 2 +circle 2 +circlew 2 +lasy 2 +nlasy 2 +amex 3 +amsy 4 +ammi 5 +amtt 6 +amsltt 6 +cmsy 2 +cmbsy 2 +cmcsc 2 +cmex 2 +circle 2 +circlew 2 +lasy 2 +nlasy 2 +cmex 3 +cmsy 4 +cmmi 5 +cmtt 6 +cmsltt 6 diff --git a/dviware/vutex/test.tex b/dviware/vutex/test.tex new file mode 100644 index 0000000000..22e78bc52d --- /dev/null +++ b/dviware/vutex/test.tex @@ -0,0 +1,68 @@ +\global\newcount\n \global\newcount\m \global\newcount\i +\nopagenumbers +\rm Roman Font + +\m=0 +\loop\ifnum\m<128 +\n=\m +\i=0 +{\loop\ifnum\i<8 \char\n\ \advance \n by 1 \advance\i by 1\repeat} +\vskip11pt\advance\m by 8\message{\the\m}\repeat +\vfill\eject +\bf Bold Face Font + +\m=0 +\loop\ifnum\m<128 +\n=\m +\i=0 +{\loop\ifnum\i<8 \char\n\ \advance \n by 1 \advance\i by 1\repeat} +\vskip11pt\advance\m by 8\message{\the\m}\repeat +\vfill\eject +\sl Slant Font + +\m=0 +\loop\ifnum\m<128 +\n=\m +\i=0 +{\loop\ifnum\i<8 \char\n\ \advance \n by 1 \advance\i by 1\repeat} +\vskip11pt\advance\m by 8\message{\the\m}\repeat +\vfill\eject +\tt Teletype Font + +\m=0 +\loop\ifnum\m<128 +\n=\m +\i=0 +{\loop\ifnum\i<8 \char\n\ \advance \n by 1 \advance\i by 1\repeat} +\vskip11pt\advance\m by 8\message{\the\m}\repeat +\vfill\eject +\font\mmi=ammi10 scaled \magstep0\mmi +Math\thinspace Italics\thinspace Font + +\m=0 +\loop\ifnum\m<128 +\n=\m +\i=0 +{\loop\ifnum\i<8 \char\n\thinspace \advance \n by 1 \advance\i by 1\repeat} +\vskip11pt\advance\m by 8\message{\the\m}\repeat +\vfill\eject +{\rm Symbols Font} +\font\msy=amsy10 scaled \magstep0\msy + +\m=0 +\loop\ifnum\m<128 +\n=\m +\i=0 +{\loop\ifnum\i<8 \char\n\thinspace\advance \n by 1 \advance\i by 1\repeat} +\vskip11pt\advance\m by 8\message{\the\m}\repeat +\vfill\eject +{\rm Extension Font} +\font\mex=amex10 scaled \magstep0\mex + +\m=0 +\loop\ifnum\m<128 +\n=\m +\i=0 +{\loop\ifnum\i<8 \char\n\thinspace\advance \n by 1 \advance\i by 1\repeat} +\vskip11pt\advance\m by 8\message{\the\m}\repeat +\bye diff --git a/dviware/vutex/vaxvms.chg b/dviware/vutex/vaxvms.chg new file mode 100644 index 0000000000..a3d7aebd2b --- /dev/null +++ b/dviware/vutex/vaxvms.chg @@ -0,0 +1,632 @@ + VMS changes file for the ASCII driver written by Warren Wolfe, Aug, 1988 + Copyright 1987 CUBE Software, Victoria, B.C., Canada + +@x [2] +@d banner=='This is ',clone,', Version 1.00' +@y +@d banner=='This is ',clone,', VAX/VMS Version 1.00' +@z + +@x +@d othercases == others: {default for cases not listed explicitly} +@d endcases == @+end {follows the default case in an extended |case| statement} +@f othercases == else +@f endcases == end +@y +@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 +@z + +@x [4] add tfm_file,gen_input,and input to program header +@p program vutex(dvi_file,bit_file,input,output); +@y +@p +@\@=[inherit('sys$library:starlet')]@>@\ + {allows us to use system symbols and routines} +program vutex(dvi_file,bit_file,tfm_file,gen_input,input,output); +@z + +@x +procedure initialize; {this procedure gets things started properly} + var i:integer; {loop index for initializations} + begin print_ln(banner);@/ +@y +@<Procedures for initialization@>@/ +procedure initialize; {this procedure gets things started properly} + var i:integer; {loop index for initializations} + begin + @<Preset initial values@>@/ + print_ln(banner);@/ +@z + +@x +Some \PASCAL\ compilers use the original name |char| for the data type +associated with the characters in text files, while other \PASCAL s +consider |char| to be a 64-element subrange of a larger data type that has +some other name. In order to accommodate this difference, we shall use +the name |text_char| to stand for the data type of the characters in the +output file. We shall also assume that |text_char| consists of +the elements |chr(first_text_char)| through |chr(last_text_char)|, +inclusive. The following definitions should be adjusted if necessary. +@^system dependencies@> + +@d text_char == char {the data type of characters in text files} +@d first_text_char=0 {ordinal number of the smallest element of |text_char|} +@d last_text_char=127 {ordinal number of the largest element of |text_char|} + +@<Types...@>= +@!text_file=packed file of text_char; +@y +Some \PASCAL\ compilers use the original name |char| for the data type +associated with the characters in text files, while other \PASCAL s +consider |char| to be a 64-element subrange of a larger data type that has +some other name. In order to accommodate this difference, we shall use +the name |text_char| to stand for the data type of the characters in the +output file. We shall also assume that |text_char| consists of +the elements |chr(first_text_char)| through |chr(last_text_char)|, +inclusive. The following definitions should be adjusted if necessary. +@^system dependencies@> + +@d text_char == char {the data type of characters in text files} +@d first_text_char=0 {ordinal number of the smallest element of |text_char|} +@d last_text_char=127 {ordinal number of the largest element of |text_char|} + +@<Types...@>= +@!text_file=text; +@z + +@x +@!byte_file=packed file of eight_bits; {files that contain binary data} +@y +{later we'll define files that contain binary data} +@z + +@x +@!dvi_file:byte_file; {the stuff we are \.{DVI}typing} +@!tfm_file:byte_file; {a font metric file} +@y +@!dvi_file:packed file of byte_block; {the stuff we are \.{DVI}typing} +@!tfm_file:packed file of byte_block; {a font metric file} +@!dvi_count:integer; {number of bytes read from current block of |dvi_file|} +@!tfm_count:integer; {number of bytes read from current block of |tfm_file|} +@!dvi_blocks:integer; {number of blocks in |dvi_file|} +@z + +@x Initialize byte counter for block in open_dvi_file +begin reset(dvi_file); +@y +begin reset(dvi_file); +dvi_count:=0; +@z + +@x And again in reopen_dvi_file +begin reset(dvi_file); +@y +begin reset(dvi_file); +dvi_count:=0; +@z + +@x +begin reset(tfm_file,cur_name); +@y +begin close(tfm_file,@=error@>:=@=continue@>); {stupid Vax/VMS run-times} +open(tfm_file,cur_name,@=old@>,@=error@>:=@=continue@>); +reset(tfm_file,@=error@>:=@=continue@>); +tfm_count:=0; +@z + +@x +begin reset(gen_input, cur_name) ; +@y +begin close(gen_input,@=error@>:=@=continue@>); +open(gen_input,cur_name,@=old@>,@=error@>:=@=message@>); +reset(gen_input,@=error@>:=@=message@>); +@z + +@x +@p function get_byte:integer; {returns the next byte, unsigned} +var b:eight_bits; +begin if eof(dvi_file) then get_byte:=0 +else begin read(dvi_file,b); incr(cur_loc); get_byte:=b; + end; +end; +@# +function signed_byte:integer; {returns the next byte, signed} +var b:eight_bits; +begin read(dvi_file,b); incr(cur_loc); +if b<128 then signed_byte:=b @+ else signed_byte:=b-256; +end; +@# +function get_two_bytes:integer; {returns the next two bytes, unsigned} +var a,@!b:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); +cur_loc:=cur_loc+2; +get_two_bytes:=a*256+b; +end; +@# +function signed_pair:integer; {returns the next two bytes, signed} +var a,@!b:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); +cur_loc:=cur_loc+2; +if a<128 then signed_pair:=a*256+b +else signed_pair:=(a-256)*256+b; +end; +@# +function get_three_bytes:integer; {returns the next three bytes, unsigned} +var a,@!b,@!c:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); +cur_loc:=cur_loc+3; +get_three_bytes:=(a*256+b)*256+c; +end; +@# +function signed_trio:integer; {returns the next three bytes, signed} +var a,@!b,@!c:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); +cur_loc:=cur_loc+3; +if a<128 then signed_trio:=(a*256+b)*256+c +else signed_trio:=((a-256)*256+b)*256+c; +end; +@# +function signed_quad:integer; {returns the next four bytes, signed} +var a,@!b,@!c,@!d:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d); +cur_loc:=cur_loc+4; +if a<128 then signed_quad:=((a*256+b)*256+c)*256+d +else signed_quad:=(((a-256)*256+b)*256+c)*256+d; +end; +@y +@d read_dvi_file(#)==begin +if dvi_count=block_size then begin +get(dvi_file,@=error:=continue@>); dvi_count:=0; +end; +#:=dvi_file^[dvi_count]; +incr(dvi_count); +end + +@p function get_byte:integer; {returns the next byte, unsigned} +var b:eight_bits; +begin if eof(dvi_file) then get_byte:=0 +else begin read_dvi_file(b); incr(cur_loc); get_byte:=b; + end; +end; +@# +function signed_byte:integer; {returns the next byte, signed} +var b:eight_bits; +begin read_dvi_file(b); incr(cur_loc); +if b<128 then signed_byte:=b @+ else signed_byte:=b-256; +end; +@# +function get_two_bytes:integer; {returns the next two bytes, unsigned} +var a,@!b:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); +cur_loc:=cur_loc+2; +get_two_bytes:=a*256+b; +end; +@# +function signed_pair:integer; {returns the next two bytes, signed} +var a,@!b:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); +cur_loc:=cur_loc+2; +if a<128 then signed_pair:=a*256+b +else signed_pair:=(a-256)*256+b; +end; +@# +function get_three_bytes:integer; {returns the next three bytes, unsigned} +var a,@!b,@!c:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c); +cur_loc:=cur_loc+3; +get_three_bytes:=(a*256+b)*256+c; +end; +@# +function signed_trio:integer; {returns the next three bytes, signed} +var a,@!b,@!c:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c); +cur_loc:=cur_loc+3; +if a<128 then signed_trio:=(a*256+b)*256+c +else signed_trio:=((a-256)*256+b)*256+c; +end; +@# +function signed_quad:integer; {returns the next four bytes, signed} +var a,@!b,@!c,@!d:eight_bits; +begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c); read_dvi_file(d); +cur_loc:=cur_loc+4; +if a<128 then signed_quad:=((a*256+b)*256+c)*256+d +else signed_quad:=(((a-256)*256+b)*256+c)*256+d; +end; +@z + +@x [37] +@ We need a function that will read in a word from the \.{TFM} file. If +the particular system +@^system dependencies@> +requires buffering, here is the place to do it. It also sets a global flag +|eof_tfm| when it reaches the end of the file. If this flag is set on +entrance to |load_tfm_file|, it is assumed that the file is bad. + +@p function tfm_integer : integer ; +var i:integer; +begin read(tfm_file, i); +eof_tfm:=eof(tfm_file); +tfm_integer:=i; +end; + +@ There is nothing wrong with defining |eof_tfm| here. + +@<Glob...@>= +@!eof_tfm:boolean; {true when end of \.{TFM} file is reached.} +@y +@ We need a function that will read in a word from the \.{TFM} file. If +the particular system +@^system dependencies@> +requires buffering, here is the place to do it. It also sets a global flag +|eof_tfm| when it reaches the end of the file. If this flag is set on +entrance to |load_tfm_file|, it is assumed that the file is bad. + +Since |tfm_file| is blocked, we need a function to read an integer +from it. + +@d read_tfm_file(#)==begin + if tfm_count=block_size then begin + get(tfm_file); tfm_count:=0; + end; + #:=tfm_file^[tfm_count]; + incr(tfm_count); + end +@# +@p function tfm_integer : integer; +var i,@!result,@!temp:integer; +begin + result := 0; + for i:=1 to 4 do begin@/ + read_tfm_file(temp); result := result*two_8+temp; + end; + eof_tfm := eof(tfm_file); + tfm_integer := result ; +end; + +@ There is nothing wrong with defining |eof_tfm| here. + +@<Glob...@>= +@!eof_tfm:boolean; {true when end of \.{TFM} file is reached.} +@z + + +@x +and |term_out| for terminal output. +@^system dependencies@> + +@<Glob...@>= +@!buffer:array[0..terminal_line_length] of ASCII_code; +@!term_in:text_file; {the terminal, considered as an input file} +@!term_out:text_file; {the terminal, considered as an output file} +@y +and |term_out| for terminal output. +@^system dependencies@> + +@d term_in==input {the terminal, considered as an input file} +@d term_out==output {the terminal, considered as an output file} + +@<Glob...@>= +@!buffer:array[0..terminal_line_length] of ASCII_code; +@!buf_length:integer; +@z + +@x +@ 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 == break(term_out) {empty the terminal output buffer} + +@ During the dialog, \vutex\ will treat the first blank space in a +line as the end of that line. Therefore |input_ln| makes sure that there +is always at least one blank space in |buffer|. +@^system dependencies@> + +@p procedure input_ln; {inputs a line from the terminal} +var k:0..terminal_line_length; +begin update_terminal; reset(term_in); +if eoln(term_in) then read_ln(term_in); +k:=0; +while (k<terminal_line_length)and not eoln(term_in) do + begin buffer[k]:=xord[term_in^]; incr(k); get(term_in); + end; +buffer[k]:=" "; +end; +@y +@ 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 == do_nothing {empty the terminal output buffer} + +@ During the dialog, \vutex\ will treat the first blank space in a +line as the end of that line. Therefore |input_ln| makes sure that there +is always at least one blank space in |buffer|. +@^system dependencies@> + +@p procedure input_ln; {inputs a line from the terminal} +var k:0..terminal_line_length; +begin update_terminal; reset(term_in) ; +k:=0; +if eoln(term_in) then write_ln +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; + end; +buffer[k]:=" "; +end; +@z + + +@x [52] +@p procedure dialog; +var k:integer; {loop variable} +begin rewrite(term_out); {prepare the terminal for output} +@<Determine the desired |start_count| values@>; +@<Determine the desired |max_pages|@>; +@<Determine the truncation |print_width|@>; +@<Determine the compression mode@>; +@<Print all the selected options@>; +if compress and (print_width > (page_width * 3 div 4)) then + page_width := 4 * print_width div 3 +else if (not compress) and (print_width > page_width - hh_offset) then + page_width := print_width + hh_offset; +if page_width > max_p_width then + begin + page_width := max_p_width; + print_width := page_width - hh_offset; + end; +num_lines := total_rast div page_width; +end; +@y + +@d batch == 0 { batch mode response} +@d online == 1 { online mode response} + +@p procedure dialog; +var i, k:integer; {loop variable} +interaction: integer; { to contain response from mode query} + +begin rewrite(term_out); {prepare the terminal for output} +if buf_length<=0 then begin + @<Determine the desired |start_count| values@>; + @<Determine the desired |max_pages|@>; + @<Determine the truncation |print_width|@>; + @<Determine the compression mode@>; + @<Print all the selected options@>; +end else @<Determine the options from the command buffer@>; +if compress and (print_width > (page_width * 3 div 4)) then + page_width := 4 * print_width div 3 +else if (not compress) and (print_width > page_width - hh_offset) then + page_width := print_width + hh_offset; +if page_width > max_p_width then + begin + page_width := max_p_width; + print_width := page_width - hh_offset; + end; +num_lines := total_rast div page_width; +end; + + +@ Here are two new sections to support getting the options from the +command line. + +@<Determine the options...@>== + begin + buffer[buf_length+1] := "?" ; { end it with a question mark } + buf_ptr := 0 ; + repeat + case buffer[buf_ptr] of + " ",",","G","g" : ; + "S","s" : begin + @<Skip to parameter@> ; k := 0 ; + @<Parse the desired |start_vals|@> ; + end ; + "P","p" : begin + @<Skip to parameter@> ; + @<Parse the desired |max_pages|@> ; + end ; + "W","w" : begin + @<Skip to parameter@> ; + @<Parse the desired |print_width|@> ; + end ; + "T","t" : compress := false ; + "D","d" : out_mode := 1 ; + othercases + print_ln('Error on command line. Skipping: ',xchr[buffer[buf_ptr]]); + @.Error on command line...:@> + endcases ; + incr(buf_ptr); + until buf_ptr >= buf_length; + @<Print all the selected options@>; +end + +@ @<Skip to parameter@>== +while (buf_ptr < buf_length) and (buffer[buf_ptr] <> " ") and + (buffer[buf_ptr] <> "=") and (buffer[buf_ptr] <> ",") do + incr(buf_ptr); +if buffer[buf_ptr] = "=" then incr(buf_ptr); +@z + + +@x [68] set up directory names +@d tfm_directory_name=='TeXfonts:' +@d tfm_directory_name_length=9 +@y +@d tfm_directory_name=='staff:[wolfe]' +@d tfm_directory_name_length=13 +@z + + +@x [122] +@d font_list_file=='nonASCII.tex.fnt' {change this to correct name} +@d list_len==16 +@y +@d font_list_file=='nonASCIItex.dat' +@d list_len==23 +@z + +@x [207] +This section should be replaced, if necessary, by changes to the program +that are necessary to make \vutex\ work at a particular installation. +Any additional routines should be inserted here. +@^system dependencies@> +@y +Here are the remaining changes to the program +that are necessary to make \.{DVItype} work on Vax/VMS. + +@<Const...@>== +@!block_size=512; +@!two_8= @'400; + +@ @<Types...@>== +@!byte_block=packed array [0..block_size-1] of 0..255; + +@ On Vax/VMS we need the following special definitions, types, variables +and procedures to be able to get the file name from the command line, +or to prompt for them. + +@d VAX_direct==@=direct@> +@d VAX_fixed==@=fixed@> +@d VAX_volatile==@=volatile@> +@d VAX_immed==@=%immed @> +@d VAX_external==@=external@> +@d VAX_stdescr==@=%stdescr @> +@d VAX_lib_get_foreign==@= lib$get_foreign@> +@d VAX_length==@=length @> +@d VAX_fab_type==@= FAB$TYPE @> +@d VAX_rab_type==@= RAB$TYPE @> +@d VAX_xab_type==@= XAB$TYPE @> +@d VAX_fab_xab==@= FAB$L_XAB @> +@d VAX_xab_nxt==@= XAB$L_NXT @> +@d VAX_xab_cod==@= XAB$B_COD @> +@d VAX_xab_fhc==@= XAB$C_FHC @> +@d VAX_xab_ebk==@= XAB$L_EBK @> + +@ @<Types...@>= +@!sixteen_bits= 0..65535; + +@ @<Glob...@>== +@!type_file: text; +@!com_line:packed array[1..300] of char; +@!cmd_len:sixteen_bits; +@!cmd_i,cmd_e,cmd_o:integer; +@!dvi_fname,@!bit_fname:varying [300] of char; +@!got_file_name: boolean; + +@ @<Preset init...@>= +open(output,'SYS$OUTPUT',@=error:=continue@>); {FIX ME! JUNK FOR RUN-TIME BUG} +cmd_i:=0; +VAX_lib_get_foreign(com_line,,cmd_len,cmd_i); +cmd_i:=1; +while (cmd_i<=cmd_len) and (com_line[cmd_i]=' ') do incr(cmd_i); + +cmd_o:=cmd_i; +while (cmd_o<cmd_len) and (com_line[cmd_o]<>'\') do incr(cmd_o); +if cmd_o<cmd_len then begin + for i:=1 to cmd_len-cmd_o do buffer[i-1] := ord(com_line[cmd_o+i]); + buf_length := cmd_len - cmd_o; + end +else buf_length := 0; +if cmd_o<cmd_len then cmd_len := cmd_o-1; + +got_file_name:=(cmd_i<=cmd_o) and (cmd_len>0); +if got_file_name then @<Parse names of files@> +else @<Input names of files@>; +open(dvi_file,dvi_fname,@=readonly@>, + @=user_action:=@>dvi_open,@=error:=continue@>); +if status(dvi_file)<>0 then begin + write_ln('Cannot open dvi file!'); + goto 9999; + end; + +open(bit_file,bit_fname,@=new,32767@>,@=error:=continue@>); +if status(bit_file)<>0 then begin + write_ln('Cannot open bit file or printer output'); + goto 9999; + end; + +@ If the name of the |dvi_file| appears in the command line, we parse it. + +@<Parse names of files@>= +begin cmd_e:=cmd_i+1; +while (cmd_e<=cmd_len) and (com_line[cmd_e]<>' ') do incr(cmd_e); +decr(cmd_e); +dvi_fname:=substr(com_line,cmd_i,cmd_e-cmd_i+1)+'.DVI'; + +cmd_i:=cmd_e+1; +while (cmd_i<=cmd_len) and (com_line[cmd_i]=' ') do incr(cmd_i); +if cmd_i<cmd_o then begin + cmd_e:=cmd_i; + while (cmd_e<=cmd_len) and (com_line[cmd_e]<>' ') do incr(cmd_e); + decr(cmd_e); + bit_fname:=substr(com_line,cmd_i,cmd_e-cmd_i+1)+'.VUT'; + end +else bit_fname:='SYS$OUTPUT'; +end + + +@ If the name of the |dvi_file| does not appear in the command line, we must +get one from the user. + +@<Input names of files@>= +begin +write('DVI file: '); +if eof then goto 9999; +read_ln(dvi_fname); + +write('VUT file: '); +if eof then goto 9999; +read_ln(bit_fname); +if bit_fname.VAX_length=0 then bit_fname:='SYS$OUTPUT'; +end + +@ Here is the library procedure that gets the user's command line. + +@<Procedures for ...@>= +[VAX_external] function VAX_lib_get_foreign( + VAX_stdescr cmdlin:[VAX_volatile] packed array [$l1..$u1:integer] of char + := VAX_immed 0; + VAX_stdescr prompt:[VAX_volatile] packed array [$l2..$u2:integer] of char + := VAX_immed 0; + var len : [VAX_volatile] sixteen_bits := VAX_immed 0; + var flag : [VAX_volatile] integer := VAX_immed 0) + :integer; extern; + +@ Here is how we intervene to find out the length of the |dvi_file|. + +@<Procedures for ...@>= +function dvi_open(var fab:VAX_fab_type; var rab:VAX_rab_type):integer; +type XAB_ptr = ^VAX_xab_type; +var user_status:integer; + xab,fhc:XAB_ptr; +begin +user_status:=@= $OPEN@>(fab); +if odd(user_status) then @= $CONNECT@>(rab); +xab:=fab.VAX_fab_xab::XAB_ptr; +fhc:=nil; +while (xab<>nil) and (fhc=nil) do + if xab^.VAX_xab_cod=VAX_xab_fhc then fhc:=xab + else xab:=xab^.VAX_xab_nxt::XAB_ptr; +if fhc<>nil then dvi_blocks:=int(fhc^.VAX_xab_ebk) +else dvi_blocks:=0; + +dvi_open:=user_status; +end; + +@z + + diff --git a/dviware/vutex/vaxvms.hlp b/dviware/vutex/vaxvms.hlp new file mode 100644 index 0000000000..70032f6308 --- /dev/null +++ b/dviware/vutex/vaxvms.hlp @@ -0,0 +1,129 @@ +Help File for vutex VMS installations: + + +syntax: +!vutex [f1] [f2] [\p1 [,p2]...)] + + +purpose: +vutex processor will print a TeX produced dvi file to an ordinary ASCII device + +f1 - name of an existing dvi file created by TeX. The string '.dvi' will be + appended to the end of the input name. + +f2 - name of a valid ASCII output device. The string '.vut' will be appended to + the end of the input name. If no file name for f2 is entered, output will + default to SYS$OUTPUT. + +If neither f2 nor f2 is entered, the user will be prompted for both. f1 must be +entered at that time, while a null response to the prompt for f2 will default +output to SYS$OUTPUT. + +p1,p2,... options to control output + +Options: + +The available options are given by: + S or s : for the starting page number + P or p : for the number of pages, beginning at S + W or w : for the width of the page in characters before truncation + T or t : for true TeX spacing as opposed to compressed + G or g : to initiate processing without further dialog + + +If no option is entered, you will be prompted for 4 parameters, all of which may +be defaulted by simply hitting RETURN. + +For example, + +$vutex out +This is vutex, DPS8/CP6 Version 0.01 +Starting page (default=*): 3 +Maximum number of pages (default=1000000): <cr> +Output truncated at column (default=160): 80 +Compress interword spacing, Y or N (default=Y): <cr> + +The first two are identical to those for dvilg8 and dvism, namely starting page +number and number of pages to type. The third prompt is for the page width in +characters (on most terminals, this is 80; on printers, this is 132). + +The fourth option allows the user to compress the spacing between words to one +space. The text for each line will begin in the proper column at the left +margin but all other TeX spacing between words will be reduced to one space. +If the user should not select the compress option, each separate word will be +left justified in the the space provided by TeX, and sup and super scripts will +usually appear on separate lines. This option is most useful for tables and +mathematical equations. + + +Other invocations would include: + +$vutex \t,w=80 +DVI file: test.dvi +VUT file: <cr> + +or + +$vutex test junk\w=132,s=4 + + + +Seeing TeX output! + +One of the more frustrating things about TeX and its precise positioning of +characters is that ``what you get is far more than what you can see'' on an +ordinary ASCII terminal. The only devices for TeX output on the mainframe have +been the laser printers, and that usually equates to running to get ( and +waiting for ) your output, and excessive waste of paper. Note that at most +installations, laser output is costed at about 10 times that produced by +ordinary hammer printers. + +So, a program has been written to allow the TeX user to preview the output +before sending it to the laser printer. Of course, the terminal screen (or an +ordinary printer) still cannot handle the fine spacing of TeX, nor can it +display all the characters you might wish to print. But, you will see an +approximation to the spacing of words across the line and lines down the page. + + +If your output was to a file, such as *vut in the example, you may copy that +file to an ordinary line printer, or you may use a screen editor to view it. +Note the following: + + 1. Some characters may not appear because they landed in a place already +occupied. Two characters from a small TeX font may fit into the space provided +on the screen, so vutex can only print one such character. + + 2. The output with true TeX spacing, may be wider than an ordinary screen +of 80 characters, or even a printer page of 132 characters. With ordinary text +you can compress the text and substantially reduce the width of the page, but +text with lots of off-line text will appear best in true TeX mode. A screen +editor may be used to view the output by switching from left to right side of +the page. The present installation limits the width to 160 characters per line. + + 3. Lines of small type or with extraordinary small interline spacing may +appear superimposed in spaces in the previous line. + + 4. \special has not been implemented and will be ignored by vutex. + + 5. If characters are missing from a line because of truncation or +superimposition, then a > sign will appear in the first position of that line. + + 6. Unprintable characters from ASCII fonts, such as Greek symbols, +and special math symbols, etc. will appear as # marks. However, as much as +possible, vutex will try to print a meaningful character to suggest what is in +the TeX output e.g. S is used to replace an integral sign. + + 7. A + sign in the first position of a line means that it contains super +scripts to the next line down, while a - sign means that it contains sub +scripts to the previous line up. + + 8. At the end of each TeX page, a ruler is drawn (in centimeters) to +indicate the horizontal scale (this will not appear on the TeX output to a +printer). Usually the vertical scale is only an approximation of the true TeX +spacing, and some pages may appear physically shorter than others. If it is +desired that all pages be the same length, perhaps with a form feed, this +alteration can be made to the code. + + +Note: In the examples given above, it is assumed that a suitable CLD file has + been implemented. (See vutex.cld on the program disk) diff --git a/dviware/vutex/vaxvms.ins b/dviware/vutex/vaxvms.ins new file mode 100644 index 0000000000..bc4647edd6 --- /dev/null +++ b/dviware/vutex/vaxvms.ins @@ -0,0 +1,20 @@ +To install vutex on your Vax VMS system: + + 1. ``weave'' vutex.web with vutex.chg to produce a printed document + + 2. check those items marked ``system dependencies'' for desired alterations + at your site. Make the necessary changes to the vutex.chg file. + For example, directory names should be changed, page_length should be + determined, and the end of page actions in the routine finish_page may be + altered to include a form feed. + + 3. ``tangle'' vutex.web with vutex.chg to produce a pascal source file + + 4. compile and link the source + + 5. alter and install the nonascii.fnt file in the desired directory. This + file lists the names of non roman tfm files with a classification code. + + 6. generate the appropriate command procedure + +That's all, folks. diff --git a/dviware/vutex/vutex.web b/dviware/vutex/vutex.web new file mode 100644 index 0000000000..c8fe0386e4 --- /dev/null +++ b/dviware/vutex/vutex.web @@ -0,0 +1,3533 @@ +% vutex:web +% +% This program was written by Warren Wolfe, and all rights are reserved. +% Copyright 1987 CUBE Software, Victoria, B.C., Canada. + +% Version 1.00 ASCII terminal driver: December 1987 +% 1.01 Modification for horizontal and vertical shifts: June 1989 +% 1.02 Modification for page action: November 1989 + +% Here is TeX material that gets inserted after \input webmac +\def\hang{\hangindent 3em\indent\ignorespaces} +\font\ninerm=amr9 +\let\mc=\ninerm % medium caps for names like PASCAL +\def\PASCAL{{\mc PASCAL}} +\def\vutex{{\bf vu\TeX}} +\def\tamu{Texas A\char38 M} +\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{{\tenrm \vutex}} +\def\contentspagenumber{0} +\def\topofcontents{\null + \def\titlepage{F} % include headline on the contents page + \def\rheader{\mainfont\hfil \contentspagenumber} + \vfill + \centerline{\titlefont The {\ttitlefont \vutex} processor} + \vskip 15pt + \centerline{(Version 1.02, Dec 1989)} + \vfill} +\def\botofcontents{ +\bigskip +\centerline{This report was written by Warren Wolfe.} +\centerline{based on the original by Tomas Rokicki} +\centerline{\copyright 1988} +} +\pageno=\contentspagenumber \advance\pageno by 1 + +@* Introduction. +This report documents the program designed to output \TeX\ processed data to any +ASCII terminal. The program is based on \.{DVIgen} which is intended as a +generic driver for \.{DVI} files created by \TeX82 and other document +preparation systems and much of this documentation is extracted directly from +the Rokicki original. It should be relatively easy to modify for a particular +system. + +\vutex\ uses the 95 printable ASCII characters and the rather limited row and +column format of an ordinary screen or printer to mimic somehow the typeset +output of \TeX\ and fine resolution printers. Understandably the result is much +less than other \.{DVIgen} processors, but it is a lot more acessible. + +The overall plan of \vutex\ is to scan the \.{DVI} file and to determine what +fonts are being used. For most of these fonts, a table is available which maps +a character in the font to a corresponding printable ASCII character. Then, +with each page, a page grid is created corresponding to the raster grid of the +screen or printer. Each character is placed in a location in that grid +according to the horizontal and vertical coordinates determined by \TeX. +Subscripts or superscripts are associated with their baseline. Finally the page +grid is output to the device in either of two forms --- compressed or \TeX\ +spaced. In the compressed mode, all extraneous spaces between words are +deleted, while the alternate mode places words so that the characters are +left-justified in the space and at the location determined by \TeX. + +@d clone=='vutex' + +@ The |banner| string defined here should be changed whenever \vutex\ +gets modified. + +@d banner=='This is ',clone,', Version 1.02' + {printed when the program starts} + +@ This program is written in standard \PASCAL, except where it is necessary +to use extensions; for example, \vutex\ must read files whose names +are dynamically specified, and that would be impossible in pure \PASCAL. +All places where nonstandard constructions are used have been listed in +the index under ``system dependencies.'' +@!@^system dependencies@> + +@d othercases == others: {default for cases not listed explicitly} +@d endcases == @+end {follows the default case in an extended |case| statement} +@f othercases == else +@f endcases == end + +@ The binary input comes from |dvi_file|, and the final output is written +to |bit_file|. On line interaction and error messages are written +on \PASCAL's standard |output| file. The term |print| is used instead of +|write| when this program writes on |output|, so that all such output +could easily be redirected if desired. + +@d print(#)==write(#) +@d print_ln(#)==write_ln(#) + +@p program vutex(dvi_file,bit_file,input,output); +label @<Labels in the outer block@>@/ +const @<Constants in the outer block@>@/ +type @<Types in the outer block@>@/ +var @<Globals in the outer block@>@/ +procedure initialize; {this procedure gets things started properly} + var i:integer; {loop index for initializations} + begin print_ln(banner);@/ + @<Set initial values@>@/ + end; + +@ If the program has to stop prematurely, it goes to the +`|final_end|'. Another label, |done|, is used when stopping normally. + +@d final_end=9999 {label for the end of it all} +@d done=30 {go here when finished with a subtask} +@d pdone=31 {go here when finished with a subtask during prescan} + +@<Labels...@>=final_end,done,pdone; + +@ The following parameters can be changed at compile time to extend or +reduce \vutex 's capacity. + +@<Constants...@>= +@!max_mem_size=68000; {the major array used for almost everything.} +@!name_size=1000; {total length of all font file names, special commands, and +other miscellaneous strings.} +@!terminal_line_length=150; {maximum number of characters input in a single + line of input from the terminal} +@!stack_size=100; {\.{DVI} files shouldn't |push| beyond this depth} +@!name_length=50; {maximum length of a file name} +@!total_rast=56000; {maximum number of rasters on page} +@!max_p_width=280; {maximum width of page in rasters} +@!dfl_p_width=200; {default width of page in rasters} +@!dfl_n_lines=280; {maximum number of lines in a page } +@!dfl_screen_width=80; {default width of screen in rasters} +@!dfl_screen_height=22; {default height of screen in rasters} +@!hor_overlap=6; {overlap for horizontal page action} +@!vert_overlap=2; {overlap for vertical page action} +@!page_length=36; {maximum physical length of a page (in cm)} +@!hh_offset=18; {offset at left side of page} +@!vv_offset=36; {offset at top of page} + +@ Here are some macros for common programming idioms. + +@d incr(#) == #:=#+1 {increase a variable by unity} +@d decr(#) == #:=#-1 {decrease a variable by unity} +@d do_nothing == {empty statement} + +@ If the \.{DVI} file is badly malformed, the whole process must be aborted; +\vutex\ will give up, after issuing an error message about the symptoms +that were noticed. + +Such errors might be discovered inside of subroutines inside of subroutines, +so a procedure called |jump_out| has been introduced. This procedure, which +simply transfers control to the label |final_end| at the end of the program, +contains the only non-local |goto| statement in \vutex. +@^system dependencies@> + +@d abort(#)==begin print_ln(' ',#); jump_out; + end +@d bad_dvi(#)==abort('Bad DVI file: ',#,'!') +@.Bad DVI file@> + +@p procedure jump_out; +begin goto final_end; +end; + +@* The character set. +Like all programs written with the \.{WEB} system, \vutex\ 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. + +@d dfl_chr == '#' {default character ASCII code for unprintable fonts} + +@<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 \vutex, +So we shall assume that the \PASCAL\ system being used for \vutex\ +has a character set containing at least the standard visible characters +of ASCII code (|"!"| through |"~"|). + +Some \PASCAL\ compilers use the original name |char| for the data type +associated with the characters in text files, while other \PASCAL s +consider |char| to be a 64-element subrange of a larger data type that has +some other name. In order to accommodate this difference, we shall use +the name |text_char| to stand for the data type of the characters in the +output file. We shall also assume that |text_char| consists of +the elements |chr(first_text_char)| through |chr(last_text_char)|, +inclusive. The following definitions should be adjusted if necessary. +@^system dependencies@> + +@d text_char == char {the data type of characters in text files} +@d first_text_char=0 {ordinal number of the smallest element of |text_char|} +@d last_text_char=127 {ordinal number of the largest element of |text_char|} + +@<Types...@>= +@!text_file=packed file of text_char; + +@ The \vutex\ 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. However, the fonts used by \TeX\ do not +all use the same character table and so we define other arrays to handle output +for the major font families. + +@<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 for tty fonts} +@!rchr: array [0..255] of text_char; + {specifies conversion of output characters for roman type fonts} +@!mchr: array [0..255] of text_char; + {specifies conversion of output characters for math italic fonts} +@!schr: array [0..255] of text_char; + {specifies conversion of output characters for math symbol fonts} +@!echr: array [0..255] of text_char; + {specifies conversion of output characters for math extension fonts} + +@ 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 @'12 do xchr[i]:=dfl_chr; +xchr[@'13]:='^'; +xchr[@'14]:='v'; +xchr[@'15]:=''''; +xchr[@'16]:=dfl_chr; +xchr[@'17]:=dfl_chr;@/ +xchr[@'20]:='i'; +xchr[@'21]:='j'; +xchr[@'22]:='`'; +xchr[@'23]:=''''; +xchr[@'24]:=' '; +xchr[@'25]:=' '; +xchr[@'26]:=' '; +xchr[@'27]:=' ';@/ +xchr[@'30]:=' '; +xchr[@'31]:=dfl_chr; +xchr[@'32]:=dfl_chr; +xchr[@'33]:=dfl_chr; +xchr[@'34]:=dfl_chr; +xchr[@'35]:=dfl_chr; +xchr[@'36]:=dfl_chr; +xchr[@'37]:=dfl_chr; +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]:=dfl_chr; + +@ Many of the \TeX\ standard fonts use a character layout slightly different +from that given above. Therefore, we define a new array to handle output +from these fonts. Modern Roman fonts are given below. + +@<Set init...@>= +for i:=0 to @'17 do rchr[i]:=dfl_chr; +rchr[@'20]:='i'; +rchr[@'21]:='j'; +rchr[@'22]:='`'; +rchr[@'23]:=''''; +rchr[@'24]:=' '; +rchr[@'25]:=' '; +rchr[@'26]:=' '; +rchr[@'27]:=' ';@/ +rchr[@'30]:=' '; +rchr[@'31]:='s'; +rchr[@'32]:='a'; +rchr[@'33]:='o'; +rchr[@'34]:='o'; +rchr[@'35]:='A'; +rchr[@'36]:='O'; +rchr[@'37]:='O';@/ +rchr[@'40]:=' '; +rchr[@'41]:='!'; +rchr[@'42]:='"'; +rchr[@'43]:='#'; +rchr[@'44]:='$'; +rchr[@'45]:='%'; +rchr[@'46]:='&'; +rchr[@'47]:='''';@/ +rchr[@'50]:='('; +rchr[@'51]:=')'; +rchr[@'52]:='*'; +rchr[@'53]:='+'; +rchr[@'54]:=','; +rchr[@'55]:='-'; +rchr[@'56]:='.'; +rchr[@'57]:='/';@/ +rchr[@'60]:='0'; +rchr[@'61]:='1'; +rchr[@'62]:='2'; +rchr[@'63]:='3'; +rchr[@'64]:='4'; +rchr[@'65]:='5'; +rchr[@'66]:='6'; +rchr[@'67]:='7';@/ +rchr[@'70]:='8'; +rchr[@'71]:='9'; +rchr[@'72]:=':'; +rchr[@'73]:=';'; +rchr[@'74]:=dfl_chr; +rchr[@'75]:='='; +rchr[@'76]:=dfl_chr; +rchr[@'77]:='?';@/ +rchr[@'100]:='@@'; +rchr[@'101]:='A'; +rchr[@'102]:='B'; +rchr[@'103]:='C'; +rchr[@'104]:='D'; +rchr[@'105]:='E'; +rchr[@'106]:='F'; +rchr[@'107]:='G';@/ +rchr[@'110]:='H'; +rchr[@'111]:='I'; +rchr[@'112]:='J'; +rchr[@'113]:='K'; +rchr[@'114]:='L'; +rchr[@'115]:='M'; +rchr[@'116]:='N'; +rchr[@'117]:='O';@/ +rchr[@'120]:='P'; +rchr[@'121]:='Q'; +rchr[@'122]:='R'; +rchr[@'123]:='S'; +rchr[@'124]:='T'; +rchr[@'125]:='U'; +rchr[@'126]:='V'; +rchr[@'127]:='W';@/ +rchr[@'130]:='X'; +rchr[@'131]:='Y'; +rchr[@'132]:='Z'; +rchr[@'133]:='['; +rchr[@'134]:='"'; +rchr[@'135]:=']'; +rchr[@'136]:='^'; +rchr[@'137]:=' ';@/ +rchr[@'140]:='`'; +rchr[@'141]:='a'; +rchr[@'142]:='b'; +rchr[@'143]:='c'; +rchr[@'144]:='d'; +rchr[@'145]:='e'; +rchr[@'146]:='f'; +rchr[@'147]:='g';@/ +rchr[@'150]:='h'; +rchr[@'151]:='i'; +rchr[@'152]:='j'; +rchr[@'153]:='k'; +rchr[@'154]:='l'; +rchr[@'155]:='m'; +rchr[@'156]:='n'; +rchr[@'157]:='o';@/ +rchr[@'160]:='p'; +rchr[@'161]:='q'; +rchr[@'162]:='r'; +rchr[@'163]:='s'; +rchr[@'164]:='t'; +rchr[@'165]:='u'; +rchr[@'166]:='v'; +rchr[@'167]:='w';@/ +rchr[@'170]:='x'; +rchr[@'171]:='y'; +rchr[@'172]:='z'; +rchr[@'173]:='-'; +rchr[@'174]:='-'; +rchr[@'175]:='"'; +rchr[@'176]:='~'; +for i:=@'177 to 255 do rchr[i]:=dfl_chr; + +@ The standard \TeX\ math italics fonts use quite a different layout than +either of the above, and we include a separate array for these useful fonts. + +@<Set init...@>= +for i:=0 to @'47 do mchr[i]:=dfl_chr; +mchr[@'50]:='-'; +mchr[@'51]:='-'; +mchr[@'52]:='-'; +mchr[@'53]:='-'; +mchr[@'54]:=' '; +mchr[@'55]:=' '; +mchr[@'56]:='>'; +mchr[@'57]:='<';@/ +mchr[@'60]:='0'; +mchr[@'61]:='1'; +mchr[@'62]:='2'; +mchr[@'63]:='3'; +mchr[@'64]:='4'; +mchr[@'65]:='5'; +mchr[@'66]:='6'; +mchr[@'67]:='7';@/ +mchr[@'70]:='8'; +mchr[@'71]:='9'; +mchr[@'72]:='.'; +mchr[@'73]:=','; +mchr[@'74]:='<'; +mchr[@'75]:='/'; +mchr[@'76]:='>'; +mchr[@'77]:='*';@/ +mchr[@'100]:=dfl_chr; +mchr[@'101]:='A'; +mchr[@'102]:='B'; +mchr[@'103]:='C'; +mchr[@'104]:='D'; +mchr[@'105]:='E'; +mchr[@'106]:='F'; +mchr[@'107]:='G';@/ +mchr[@'110]:='H'; +mchr[@'111]:='I'; +mchr[@'112]:='J'; +mchr[@'113]:='K'; +mchr[@'114]:='L'; +mchr[@'115]:='M'; +mchr[@'116]:='N'; +mchr[@'117]:='O';@/ +mchr[@'120]:='P'; +mchr[@'121]:='Q'; +mchr[@'122]:='R'; +mchr[@'123]:='S'; +mchr[@'124]:='T'; +mchr[@'125]:='U'; +mchr[@'126]:='V'; +mchr[@'127]:='W';@/ +mchr[@'130]:='X'; +mchr[@'131]:='Y'; +mchr[@'132]:='Z'; +mchr[@'133]:=dfl_chr; +mchr[@'134]:=dfl_chr; +mchr[@'135]:=dfl_chr; +mchr[@'136]:=dfl_chr; +mchr[@'137]:=dfl_chr;@/ +mchr[@'140]:='l'; +mchr[@'141]:='a'; +mchr[@'142]:='b'; +mchr[@'143]:='c'; +mchr[@'144]:='d'; +mchr[@'145]:='e'; +mchr[@'146]:='f'; +mchr[@'147]:='g';@/ +mchr[@'150]:='h'; +mchr[@'151]:='i'; +mchr[@'152]:='j'; +mchr[@'153]:='k'; +mchr[@'154]:='l'; +mchr[@'155]:='m'; +mchr[@'156]:='n'; +mchr[@'157]:='o';@/ +mchr[@'160]:='p'; +mchr[@'161]:='q'; +mchr[@'162]:='r'; +mchr[@'163]:='s'; +mchr[@'164]:='t'; +mchr[@'165]:='u'; +mchr[@'166]:='v'; +mchr[@'167]:='w';@/ +mchr[@'170]:='x'; +mchr[@'171]:='y'; +mchr[@'172]:='z'; +mchr[@'173]:='i'; +mchr[@'174]:='j'; +mchr[@'175]:='p'; +mchr[@'176]:=' '; +mchr[@'177]:=' ';@/ +for i:=@'200 to 255 do mchr[i]:=dfl_chr; + +@ The math symbol font is, for the most part, unprintable in ASCII, but we +shall map some of the characters onto meaningful representations. + +@<Set init...@>= +schr[0]:='-'; +schr[1]:='.'; +schr[2]:='x'; +schr[3]:='*'; +schr[4]:=':'; +schr[5]:=dfl_chr; +schr[6]:='+'; +schr[7]:='-';@/ +schr[@'10]:='+'; +schr[@'11]:='-'; +schr[@'12]:='x'; +schr[@'13]:='o'; +schr[@'14]:='o'; +schr[@'15]:='O'; +schr[@'16]:='o'; +schr[@'17]:='o';@/ +schr[@'20]:='='; +schr[@'21]:='='; +schr[@'22]:='<'; +schr[@'23]:='>'; +schr[@'24]:='<'; +schr[@'25]:='>'; +schr[@'26]:='<'; +schr[@'27]:='>';@/ +schr[@'30]:='~'; +schr[@'31]:='~'; +schr[@'32]:='<'; +schr[@'33]:='>'; +schr[@'34]:='<'; +schr[@'35]:='>'; +schr[@'36]:='<'; +schr[@'37]:='>';@/ +schr[@'40]:='-'; +schr[@'41]:='-'; +schr[@'42]:='|'; +schr[@'43]:='|'; +schr[@'44]:='-'; +schr[@'45]:='/'; +schr[@'46]:='\'; +schr[@'47]:='~';@/ +schr[@'50]:='-'; +schr[@'51]:='-'; +schr[@'52]:='|'; +schr[@'53]:='|'; +schr[@'54]:='-'; +schr[@'55]:='\'; +schr[@'56]:='/'; +schr[@'57]:=dfl_chr;@/ +schr[@'60]:=''''; +schr[@'61]:=dfl_chr; +schr[@'62]:=dfl_chr; +schr[@'63]:=dfl_chr; +schr[@'64]:=dfl_chr; +schr[@'65]:=dfl_chr; +schr[@'66]:='/'; +schr[@'67]:=dfl_chr;@/ +schr[@'70]:=dfl_chr; +schr[@'71]:=dfl_chr; +schr[@'72]:='-'; +schr[@'73]:='0'; +schr[@'74]:='R'; +schr[@'75]:='I'; +schr[@'76]:='|'; +schr[@'77]:='|';@/ +schr[@'100]:=dfl_chr; +schr[@'101]:='A'; +schr[@'102]:='B'; +schr[@'103]:='C'; +schr[@'104]:='D'; +schr[@'105]:='E'; +schr[@'106]:='F'; +schr[@'107]:='G';@/ +schr[@'110]:='H'; +schr[@'111]:='I'; +schr[@'112]:='J'; +schr[@'113]:='K'; +schr[@'114]:='L'; +schr[@'115]:='M'; +schr[@'116]:='N'; +schr[@'117]:='O';@/ +schr[@'120]:='P'; +schr[@'121]:='Q'; +schr[@'122]:='R'; +schr[@'123]:='S'; +schr[@'124]:='T'; +schr[@'125]:='U'; +schr[@'126]:='V'; +schr[@'127]:='W';@/ +schr[@'130]:='X'; +schr[@'131]:='Y'; +schr[@'132]:='Z'; +schr[@'133]:=dfl_chr; +schr[@'134]:=dfl_chr; +schr[@'135]:=dfl_chr; +schr[@'136]:=dfl_chr; +schr[@'137]:=dfl_chr;@/ +schr[@'140]:='-'; +schr[@'141]:='-'; +schr[@'142]:='|'; +schr[@'143]:='|'; +schr[@'144]:='|'; +schr[@'145]:='|'; +schr[@'146]:='{'; +schr[@'147]:='}';@/ +schr[@'150]:='<'; +schr[@'151]:='>'; +schr[@'152]:='|'; +schr[@'153]:='|'; +schr[@'154]:='|'; +schr[@'155]:='|'; +schr[@'156]:='\'; +schr[@'157]:=dfl_chr;@/ +schr[@'160]:='|'; +schr[@'161]:=dfl_chr; +schr[@'162]:=dfl_chr; +schr[@'163]:='S'; +schr[@'164]:=dfl_chr; +schr[@'165]:=dfl_chr; +schr[@'166]:='<'; +schr[@'167]:='>';@/ +for i:=@'170 to @'176 do schr[i]:=dfl_chr; +for i:=@'177 to 255 do schr[i]:=dfl_chr; + +@ And then there is the math extension font which is used to create extended +symbols. We do the best that we can. + +@<Set init...@>= +echr[0]:='('; +echr[1]:=')'; +echr[2]:='['; +echr[3]:=']'; +echr[4]:='|'; +echr[5]:='|'; +echr[6]:='|'; +echr[7]:='|';@/ +echr[@'10]:='{'; +echr[@'11]:='}'; +echr[@'12]:='<'; +echr[@'13]:='>'; +echr[@'14]:='|'; +echr[@'15]:='|'; +echr[@'16]:='/'; +echr[@'17]:='\';@/ +echr[@'20]:='('; +echr[@'21]:=')'; +echr[@'22]:='('; +echr[@'23]:=')'; +echr[@'24]:='['; +echr[@'25]:=']'; +echr[@'26]:='|'; +echr[@'27]:='|';@/ +echr[@'30]:='|'; +echr[@'31]:='|'; +echr[@'32]:='{'; +echr[@'33]:='}'; +echr[@'34]:='<'; +echr[@'35]:='>'; +echr[@'36]:='/'; +echr[@'37]:='\';@/ +echr[@'40]:='('; +echr[@'41]:=')'; +echr[@'42]:='['; +echr[@'43]:=']'; +echr[@'44]:='|'; +echr[@'45]:='|'; +echr[@'46]:='|'; +echr[@'47]:='|';@/ +echr[@'50]:='{'; +echr[@'51]:='}'; +echr[@'52]:='<'; +echr[@'53]:='>'; +echr[@'54]:='/'; +echr[@'55]:='\'; +echr[@'56]:='/'; +echr[@'57]:='\';@/ +echr[@'60]:='('; +echr[@'61]:=')'; +echr[@'62]:='|'; +echr[@'63]:='|'; +echr[@'64]:='|'; +echr[@'65]:='|'; +echr[@'66]:='|'; +echr[@'67]:='|';@/ +echr[@'70]:='('; +echr[@'71]:=')'; +echr[@'72]:='('; +echr[@'73]:=')'; +echr[@'74]:='{'; +echr[@'75]:='}'; +echr[@'76]:='|'; +echr[@'77]:='|';@/ +echr[@'100]:='('; +echr[@'101]:=')'; +echr[@'102]:='|'; +echr[@'103]:='|'; +echr[@'104]:='<'; +echr[@'105]:='>'; +echr[@'106]:=dfl_chr; +echr[@'107]:=dfl_chr;@/ +echr[@'110]:='S'; +echr[@'111]:='S'; +echr[@'112]:='o'; +echr[@'113]:='O'; +echr[@'114]:='+'; +echr[@'115]:='+'; +echr[@'116]:='x'; +echr[@'117]:='X';@/ +echr[@'120]:='E'; +echr[@'121]:=dfl_chr; +echr[@'122]:='S'; +for i:=@'123 to @'127 do echr[i]:=dfl_chr; +echr[@'130]:='E'; +echr[@'131]:=dfl_chr; +echr[@'132]:='S'; +for i:=@'133 to @'141 do echr[i]:=dfl_chr; +echr[@'142]:='^'; +echr[@'143]:='^'; +echr[@'144]:='^'; +echr[@'145]:='~'; +echr[@'146]:='~'; +echr[@'147]:='~';@/ +echr[@'150]:='['; +echr[@'151]:=']'; +echr[@'152]:='|'; +echr[@'153]:='|'; +echr[@'154]:='|'; +echr[@'155]:='|'; +echr[@'156]:='{'; +echr[@'157]:='}';@/ +echr[@'160]:='|'; +echr[@'161]:='|'; +echr[@'162]:='|'; +echr[@'163]:='|'; +echr[@'164]:='|'; +echr[@'165]:='|'; +echr[@'166]:='|'; +echr[@'167]:='|';@/ +echr[@'170]:='|'; +echr[@'171]:='\'; +echr[@'172]:=dfl_chr; +echr[@'173]:=dfl_chr; +echr[@'174]:=dfl_chr; +echr[@'175]:=dfl_chr; +echr[@'176]:='|'; +echr[@'177]:='|';@/ +for i:=@'200 to 255 do echr[i]:=dfl_chr; + +@ The following system-independent code makes the |xord| array contain a +suitable inverse to the information in |xchr|. + +@<Set init...@>= +for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40; +for i:=" " to "~" do xord[xchr[i]]:=i; + +@* Device-independent file format. +Before we get into the details of \vutex, 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 \vutex\ 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. \vutex\ 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 postable. + +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|. +\vutex\ prints this comment out, which, in \TeX82 files, contains the date +and time of execution. + +@d id_byte=2 {identifies the kind of \.{DVI} files described here} + +@ Font definitions for a given font number |k| contain further parameters +$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$ +The four-byte value |c| is the check sum that \TeX\ (or whatever program +generated the \.{DVI} file) found in the \.{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. \vutex\ does not make use of this capability, +however. +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. +Unfortunately, since characters can be set outside of the page boundaries, +these numbers cannot be used to set limits on the page area. + +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@> + +@* Plan of attack. + +A \.{DVI} to device program must deal with a large amount of data. The +\.{DVI} files in most applications tend to be quite large. +For this reason, one of the primary goals in the design of this program was +to reduce memory requirements as much as possible. After much consideration +and examination of several other \.{DVI} drivers, it was decided that operation +of this program would occur in three distinct phases---prescan, font loading, +and page drawing. + +Every \.{DVI} file is prescanned. During this phase, information about font +usage is collected. A very simple interpreter simply notices the |bop| and +|eop| tokens (to allow a subset of the pages to be printed), all of the +|font_def| commands (the name and other information is stored), and the +|set_char| commands (certain counters are incremented). For every font that is +declared, space is allocated on the main |mem| array for a table of information +regarding that font. This table contains that information that is necessary as +determined from the \TeX\ font metric (\.{TFM}) files. + +During the second phase, the fonts are given a priority rating and the \.{TFM} +information table is created. At this point, all the information about +font usage in the file is located in the |mem| array. Therefore, a +routine named |base_font| is called. This routine scans the declared fonts +and assesses them as either printable ASCII fonts or as unprintable fonts. +The printable fonts are given priority ratings based on the use of characters in +each font. The font with priority 0 is the |base_font| and the horizontal +raster resolution is based on spacings determined by that font. + +The vertical raster resolution is determined by the arbitrary assignment of a +set number of lines to the printed page. Each character or word is then +assigned a line in the page according to its vertical coordinate. + +The routine |load_tfm_file| creates a table for each font, declaring the width +of each character in \.{DVI} units. Then a companion table is created with a +raster unit width for each character. + +During phase three, the \.{DVI} file is reread one page at a time, and the +characters are ``drawn'' onto a |page|. This is a character array which +will be output to the device when the page is completed. Because of the coarse +raster resolution of the device, several characters may be placed within the +same array space, but the priority rating is used to determine which character +shall appear there. Thus, a |priority| array is kept to determine the +priority of any character appearing on the |page|. Also, because of +differences in \TeX\ font spacing, blank spaces may appear in words if the +characters were placed exactly in their raster positions. Instead, a |word| +array positions a sequence of characters into adjacent raster positions and then +attempts to reset the sequence into the space allocated by \TeX\ in the +|page|. Such words are left-justified in the allocated space so that +large spaces (or, possibly, one space) will appear between words in the +|page|. + +We note that, if a sequence of characters is set from a font with smaller +character sizes than the |base_font|, then the space allocated by \TeX\ for that +word will be smaller than the raster width required. Hence, words consisting of +characters from small fonts may be truncated. + +\vutex\ output is in one of two modes --- compressed or pure \TeX. In the pure +\TeX\ mode, each page is printed as it was constructed in the array, i.e. each +word is left-justified in the space provided by the \.{DVI} commands. Again, +a number of spaces may appear between word, and the output can require a wide +palette. Sub/super script lines will appear on separate associated lines. + +The compression mode will shrink inter-word spacing to one space, with the first +character on each line appearing in its proper \TeX\ position, and all +subsequent words are shifted to the left. Difficulties in associating sub/super +script lines with the proper word in the baseline has forced a vertical +compression of the associated lines into the baseline before the horizontal +compression takes place. This may lead to ambiguous text, particularly with +mathematical expressions, but it is recommended that such text be viewed in the +unaltered non-compressed mode. + +The format of the |mem| array reflects these phases. It is organized as +a large array of 32-bit words. The first 256 words are used as font pointers. +They are initialized to zero at the beginning of the program. Then, whenever +font number |k| is defined, 262 words are allocated in the |mem| array for the +font, and |mem[k]| is set to point to the sixth word of the allocated space. + +The first allocated word, or |mem[mem[k]-6]|, consists of two sixteen bit +integers. The least significant sixteen bits are the external or directory +size of the font. The high order sixteen bits contain a pointer to the |names| +array, which contains the name of the font. + +The next word (|mem[mem[k]-5]|) contains the |font_scaled_size| followed by the +font design size. Then comes the |font_space|. The next word is |font_type| +which determines the output array to use, followed by |font_status| to indicate +if the font is a printable font or not. + +Finally comes the tabular information for the individual characters. They each +use two words. The first word, |mem[mem[k]+2*f]|, contains the |tfm_width| of +the character in \.{DVI} units. The next contains the |use_count| for the first +pass, and the |raster_width| during the second pass. + +During the first pass, the first word of the first character in the table +contains the checksum of the \.{TFM} file loaded from the \.{DVI} file. +Hopefully this will match the checksum in the \.{TFM} file. + +The |names| array will contain all of the string information in the file. It +is used as the input buffer from the terminal, the storage area for font names, +temporary space for special's, etc. A `string' consists of a pointer into +this array. The end of every string will be the value 0 as a marker. +|next_names_free| indicates the end of the table. + +@ The following macroes should make the program slightly more readable, as they +calculate the offsets, etc. automatically. + +@d font_desc_size=262 +@d hi(#)==(# div 65536) +@d lo(#)==(# mod 65536) +@d raster_round(#)==round((#)/resol) + hh_offset +@d line_round(#)==round((#)/vresol) + vv_offset +@d set_line(#)== + if line_for[#]<0 then + begin line_for[#] := next_line_free; + next_line_free:=next_line_free + page_width; + end +@d @!font_check_sum == mem[cur_fptr] {Temporary location for checksum} +@d @!font_scaled_size == mem[cur_fptr-5] {Scaled size for current font} +@d @!font_design_size == mem[cur_fptr-4] {Design size for current font} +@d @!font_space == mem[cur_fptr-3] {Size of a thinspace in the font} +@d @!font_type == mem[cur_fptr-2] {Code to determine type of font} +@d @!declared == 1 +@d @!other == 2 +@d @!mexten == 3 +@d @!msymbol == 4 +@d @!mitalic == 5 +@d @!tty == 6 +@d @!roman == 7 +@d @!font_status == mem[cur_fptr-1] {Priority of the current font} +@d @!font_nsave == mem[cur_fptr-6] {For storing the name of the font} +@d @!font_name == hi(mem[cur_fptr-6]) {High sixteen bits for font name ptr} +@d @!directory_size == lo(mem[cur_fptr-6]) {Low sixteen bits for external + size pointer} +@d @!tfm_width == mem[cur_char_ptr] {\.{TFM} width of current character} +@d @!use_count == mem[cur_char_ptr+1] {Use count used mostly in phase two.} +@d @!raster_width == mem[cur_char_ptr+1] {character width in rasters, phase + three} +@d @!temp_ar(#) == mem[next_mem_free+#] {`array' used for temporary storage} + +@ We also need to define some of these variables, and initialize them. + +@<Glob...@>= +@!mem : array [0..max_mem_size] of integer ; {major memory array} +@!names : array [0..name_size] of 0..127 ; {string array} +@!cur_fptr, @!cur_char_ptr : integer ; {temporary pointers} +@!next_mem_free, @!next_names_free : integer; { used for allocation } +i,j : integer ; {plain old index variable} + +@ @<Set init...@>= +for i := 0 to 255 do + mem[i] := 0 ; { no fonts defined yet.} +next_mem_free := 256 ; { next available position in mem array } +next_names_free := 1 ; { next available position in names array } + +@* Input from binary files. + +@<Types...@>= +@!eight_bits=0..255; {unsigned one-byte quantity} +@!byte_file=packed file of eight_bits; {files that contain binary data} +@!word_file=packed file of integer; {for pixel file words} + +@ The program deals with two binary file variables: |dvi_file| is the main input +file that we are translating into symbolic form, and |tfm_file| is the current +\.{TFM} file from which character raster information is being read. The third +file is the final output file, a text file. + +@<Glob...@>= +@!dvi_file:byte_file; {the stuff we are \.{DVI}typing} +@!tfm_file:byte_file; {a font metric file} +@!bit_file:text_file; {where the final output goes} +@^system dependencies@> + +@ To prepare these files for input, we |reset| them. An extension of +\PASCAL\ is needed in the case of |tfm_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. If |eof(f)| is true immediately after +|reset(f,s)| has acted, we assume that no file named |s| is accessible. +@^system dependencies@> + +@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|} +begin reset(dvi_file); +cur_loc:=0; +end; +@# +procedure reopen_dvi_file; {reopens the |dvi_file| for the next scan} +begin reset(dvi_file); +cur_loc:=0; +end; +@# +procedure open_bit_file; {prepares final output for writing} +begin rewrite(bit_file); +bit_is_open := true ; +end; +@# +procedure open_tfm_file; {opens \.{TFM} file} +begin reset(tfm_file,cur_name); +eof_tfm := eof(tfm_file); +end; +@# +procedure open_input_text ; {prepares to read from general purpose input} +begin reset(gen_input, cur_name) ; +end ; + +@ |cur_loc| and |cur_name| are 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 \.{TFM} file name before |open_tfm_file| is +called. + +@<Glob...@>= +@!cur_loc:integer; {where we are about to look, in |dvi_file|} +@!cur_name:packed array[1..name_length] of char; {external name, + with no lower case letters} + +@ We shall use another set of simple functions to read the next byte or +bytes from |dvi_file|. There are seven possibilities, each of which is +treated as a separate function in order to minimize the overhead for +subroutine calls. +@^system dependencies@> + +@p function get_byte:integer; {returns the next byte, unsigned} +var b:eight_bits; +begin if eof(dvi_file) then get_byte:=0 +else begin read(dvi_file,b); incr(cur_loc); get_byte:=b; + end; +end; +@# +function signed_byte:integer; {returns the next byte, signed} +var b:eight_bits; +begin read(dvi_file,b); incr(cur_loc); +if b<128 then signed_byte:=b @+ else signed_byte:=b-256; +end; +@# +function get_two_bytes:integer; {returns the next two bytes, unsigned} +var a,@!b:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); +cur_loc:=cur_loc+2; +get_two_bytes:=a*256+b; +end; +@# +function signed_pair:integer; {returns the next two bytes, signed} +var a,@!b:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); +cur_loc:=cur_loc+2; +if a<128 then signed_pair:=a*256+b +else signed_pair:=(a-256)*256+b; +end; +@# +function get_three_bytes:integer; {returns the next three bytes, unsigned} +var a,@!b,@!c:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); +cur_loc:=cur_loc+3; +get_three_bytes:=(a*256+b)*256+c; +end; +@# +function signed_trio:integer; {returns the next three bytes, signed} +var a,@!b,@!c:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); +cur_loc:=cur_loc+3; +if a<128 then signed_trio:=(a*256+b)*256+c +else signed_trio:=((a-256)*256+b)*256+c; +end; +@# +function signed_quad:integer; {returns the next four bytes, signed} +var a,@!b,@!c,@!d:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d); +cur_loc:=cur_loc+4; +if a<128 then signed_quad:=((a*256+b)*256+c)*256+d +else signed_quad:=(((a-256)*256+b)*256+c)*256+d; +end; + +@* Reading the font information. +\.{DVI} file format does not include information about character widths, since +that would tend to make the files a lot longer. But a program that reads a +\.{DVI} file is supposed to know the widths of the characters that appear in +\\{set\_char} commands. Therefore \vutex\ reads in a \.{TFM} file for each +font that is used. + +@ It is, of course, a simple matter to print the name of a given font. + +@p procedure print_font(@!f:integer); +var k:0..name_size; {index into |names|} + cur_fptr : integer ; +begin cur_fptr := mem[f]; if font_name=0 then print_ln('UNDEFINED!') +@.UNDEFINED@> +else begin k := font_name ; + if names[k]>0 then begin + while names[k]>0 do begin + print(xchr[names[k]]) ; + k := k + 1 ; + end ; + print(':'); + end ; + k := k + 1 ; + while names[k]>0 do begin + print(xchr[names[k]]); + k := k + 1 ; + end ; + print_ln(' '); + end; +end; + +@ We need a function that will read in a word from the \.{TFM} file. If +the particular system +@^system dependencies@> +requires buffering, here is the place to do it. It also sets a global flag +|eof_tfm| when it reaches the end of the file. If this flag is set on +entrance to |load_tfm_file|, it is assumed that the file is bad. + +@p function tfm_integer : integer ; +var i:integer; +begin read(tfm_file, i); +eof_tfm:=eof(tfm_file); +tfm_integer:=i; +end; + +@ There is nothing wrong with defining |eof_tfm| here. + +@<Glob...@>= +@!eof_tfm:boolean; {true when end of \.{TFM} file is reached.} + + +@ The most important part of |load_tfm_file| is the conversion to \.{DVI} units, +which involves multiplying the relative values in the \.{TFM} 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 \.{TFM} +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. + +@p function tfm_to_int(val:integer) : integer ; +var alpha, beta, x, z:integer ; {help in converting widths} + b0, b1, b2, b3:eight_bits ; {temporary byte storage} +begin z := font_scaled_size ; +@<Split tfm value up into bytes@>; +@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>; + z:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta ; + if b0=255 then z := z - alpha ; + tfm_to_int := z ; +end ; + +@ @<Split tfm value up...@>= +begin x:=val ; +if x>=0 then b0:=x div @'100000000 +else begin x:=(x+@'10000000000)+@'10000000000; b0:=x div @'100000000+128; + end ; +x:=x mod @'100000000; +b1:=x div @'200000;x:=x mod @'200000;b2:=x div @'400;b3:=x mod @'400; end; + +@ @<Replace |z|...@>= +begin alpha:=16*z; beta:=16; +while z>=@'40000000 do + begin z:=z div 2; beta:=beta div 2; + end; +end ; + +@ And, finally, we have the procedure which will extract the information from +the \.{TFM} file. This routine is for input from the \.{TFM} file, which is (or +should be) a |packed file of integer|, so this should cause no grief. See the +documentation on |tftopl| for the structure of the file. + + +@ @p procedure load_tfm_file ; {read in the width data} +label 9997, {used for bad format} + 9999; {used for normal exit} +var r:integer; {used for |cur_name| manipulation} + i, k : integer ; {used for general pointer} + bc, ec : integer ; {first and last characters in font} + @!ptr : integer; {pointer to width table} + +begin + @<Move tfm name into the |cur_name| string@> ; + if out_mode > 0 then print_ln('Trying to load ',cur_name) ; + open_tfm_file ; + k := next_mem_free ; + if eof_tfm then goto 9997 ; + while not eof_tfm do begin + mem[k] := tfm_integer ; + k := k + 1 ; + if k > max_mem_size then + abort(clone,' memory size exceeded on load of tfm file!') ; + end ; + bc := hi(mem[next_mem_free+1]) ; + ec := lo(mem[next_mem_free+1]) ; + if mem[next_mem_free+6]<>font_check_sum then + print_ln('Checksum in tfm file does not match that in dvi file!') ; + k := next_mem_free + lo(mem[next_mem_free]) + 6 - bc ; + cur_char_ptr := cur_fptr ; + for i := 0 to bc-1 do begin + tfm_width := 0 ; + cur_char_ptr := cur_char_ptr + 2 ; + end ; + for i := bc to ec do begin + if mem[k+i] > 0 then + ptr := mem[k+i] div @'100000000 + else + ptr := ((mem[k+i] + one_fourth) + one_fourth) div @'100000000 + 128 ; + tfm_width := tfm_to_int(mem[ptr + ec + k + 1 ]) ; + cur_char_ptr := cur_char_ptr + 2 ; + end ; + for i := ec+1 to 127 do begin + tfm_width := 0 ; + cur_char_ptr := cur_char_ptr + 2 ; + end ; + goto 9999 ; +9997: print_ln('---not loaded, TFM file is bad') ; +9999: end ; + +@* User interface. +\vutex\ is normally considered a filter, and therefore operates very +quietly unless some error is encountered. However, for debugging purposes +or the inquisitive, one can access the variable |out_mode|. This variable +will allow the tracing of \vutex. It has two possible values: 0, and 1. +If it is zero, then the program is very quiet, only informing the user of +errors. If it is one, a trace is executed for debugging purposes. + +There are a few optional parameters requested when this program is invoked. It +is possible to print only a restricted subset of the pages by specifying the +desired starting page and the maximum number of pages. You also may select the +number of rasters across a page and the compression mode for 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, +\vutex\ will begin with the earliest such page in the file. The +default specification `\.*' (which matches all pages) therefore denotes +the page at the beginning of the file. + +When \vutex\ begins, it engages the user in a brief dialog so that the +options will be specified. This part of \vutex\ requires nonstandard \PASCAL\ +constructions to handle the online interaction; so it may be preferable in some +cases to omit the dialog and simply to stick to the default options +(|out_mode=terse|, starting page `\.*', |max_pages=1000000|, |print_width=160|, +and |compress=false|). On other hand, the system-dependent routines that are +needed are not complicated, so it will not be terribly difficult to introduce +them. Normally, however, the necessary parameters should be taken from the +command line which invokes the program (if this is possible.) Then, this part +of the program would not even be used. This is in the domain of the change +file, however. + +@^system dependencies@> + +@d errors_only=0 {value of |out_mode| when minimal printing occurs} +@d terse=1 {value of |out_mode| for abbreviated output} + +@<Glob...@>= +@!out_mode:errors_only..terse; {controls the amount of output} +@!max_pages:integer; {at most this many |bop..eop| pages will be printed} +@!page_width:integer; {page width for setting characters} +@!num_lines:integer; {number of lines in a page} +@!print_width: integer; {the number of raster units across screen before + truncation} +@!print_height: integer; {the number of lines printed on each screen} +@!compress: boolean; {true if interword spacing to be compressed to one blank} +@!batch_mode: boolean; {true if pages to be processed with no interaction} + +@ 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...@>= +out_mode := errors_only; +max_pages:=1000000; start_vals:=0; start_there[0]:=false; +page_width:= dfl_p_width; num_lines:=dfl_n_lines; +print_width:= dfl_screen_width; print_height:= dfl_screen_height; +compress:=false;batch_mode:=false; + +@ Here is a simple subroutine that tests if the current page might be the +starting page. + +@p function start_match:boolean; {does |count| match the starting spec?} +var k:0..9; {loop index} +@!match:boolean; {does everything match so far?} +begin match:=true; +for k:=0 to start_vals do + if start_there[k]and(start_count[k]<>count[k]) then match:=false; +start_match:=match; +end; + +@ The |input_ln| routine waits for the user to type a line at his or her +terminal; then it puts ASCII-code equivalents for the characters on that line +into the |buffer| array. The |term_in| file is used for terminal input, +and |term_out| for terminal output. +@^system dependencies@> + +@<Glob...@>= +@!buffer:array[0..terminal_line_length] of ASCII_code; +@!term_in:text_file; {the terminal, considered as an input file} +@!term_out:text_file; {the terminal, considered as an output file} + +@ Since the terminal is being used for both input and output, some systems +need a special routine to make sure that the user can see a prompt message +before waiting for input based on that message. (Otherwise the message +may just be sitting in a hidden buffer somewhere, and the user will have +no idea what the program is waiting for.) We shall call a system-dependent +subroutine |update_terminal| in order to avoid this problem. +@^system dependencies@> + +@d update_terminal == break(term_out) {empty the terminal output buffer} + +@ During the dialog, \vutex\ will treat the first blank space in a +line as the end of that line. Therefore |input_ln| makes sure that there +is always at least one blank space in |buffer|. +@^system dependencies@> + +@p procedure input_ln; {inputs a line from the terminal} +var k:0..terminal_line_length; +begin update_terminal; reset(term_in); +if eoln(term_in) then read_ln(term_in); +k:=0; +while (k<terminal_line_length)and not eoln(term_in) do + begin buffer[k]:=xord[term_in^]; incr(k); get(term_in); + end; +buffer[k]:=" "; +end; + +@ The global variable |buf_ptr| is used while scanning each line of input; +it points to the first unread character in |buffer|. + +@<Glob...@>= +@!buf_ptr:0..terminal_line_length; {the number of characters read} + +@ Here is a routine that scans a (possibly signed) integer and computes +the decimal value. If no decimal integer starts at |buf_ptr|, the +value 0 is returned. The integer should be less than $2^{31}$ in +absolute value. + +@p function get_integer : integer; +var x:integer; {accumulates the value} +@!negative:boolean; {should the value be negated?} +begin if buffer[buf_ptr]="-" then + begin negative:=true; incr(buf_ptr); + end +else negative:=false; +x:=0; +while (buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9") do + begin x:=10*x+buffer[buf_ptr]-"0"; incr(buf_ptr); + end; +if negative then get_integer:=-x @+ else get_integer:=x; +end; + +@ The selected options are put into global variables by the |dialog| +procedure, which is called just as \vutex\ begins. +@^system dependencies@> + +@p procedure dialog; +var k:integer; {loop variable} + dum_width:integer; + dum_height:integer; +begin rewrite(term_out); {prepare the terminal for output} +dum_width := 0; dum_height := 0; +@<Determine the desired interactive mode@>; +@<Determine the desired |start_count| values@>; +@<Determine the desired |max_pages|@>; +@<Determine the number of printed rasters |dum_width|@>; +@<Determine the number of printed rows |dum_height|@>; +@<Determine the compression mode@>; +@<Print all the selected options@>; +@<Print the page action options@>; +@<Adjust page dimensions@>; +end; + +@ @<Determine the desired interactive mode@>= +write(term_out,'Interactive mode (default=Y): '); +batch_mode := false; input_ln; buf_ptr:=0; +if (buffer[buf_ptr] = "N") or (buffer[buf_ptr] = "n") then batch_mode:=true ; + +@ @<Determine the desired |start...@>= +write(term_out,'Starting page (default=*): '); +start_vals:=0; start_there[0]:=false; +input_ln; buf_ptr:=0; k:=0; +@<Parse the desired |start_vals|@> + +@ These two routines have been split in half so it is easier to accept these +options from the command line if your operating system makes that possible. +Put a question mark at the end of the line to end it when doing it this way. + +@<Parse the desired |start_vals|@>= +if (buffer[buf_ptr] <> "?") and (buffer[buf_ptr] <> " ") and + (buffer[buf_ptr] <> "/") then begin + 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]=" ") or (buffer[buf_ptr]="/") or + (buffer[buf_ptr]="?") then + start_vals:=k + else begin k := 0 ; start_vals := 0 ; end ; + until start_vals=k ; +end + +@ @<Determine the desired |max_pages|@>= +write(term_out,'Maximum number of pages (default=1000000): '); +max_pages := 1000000; input_ln; buf_ptr:=0; +@<Parse the desired |max_pages|@> + +@ @<Parse the desired |max_pages|@>= + begin max_pages:=get_integer; + if max_pages=0 then + max_pages := 1000000 ; + end + +@ @<Determine the number of printed rasters |dum_width|@>= +write(term_out,'Width of output in characters', + ' (default=160/batch, 80/interactive): '); +input_ln; buf_ptr:=0; dum_width:=get_integer + +@ @<Determine the number of printed rows |dum_height|@>= +write(term_out,'Number of printed lines per screen', + ' (default=280/batch, 22/interactive): '); +input_ln; buf_ptr:=0; +dum_height:=get_integer + +@ @<Determine the compression mode@>= +write(term_out,'Compress interword spaces, Y or N (default=N): '); +input_ln; buf_ptr:=0; compress:=false ; +if (buffer[buf_ptr] = "Y") or (buffer[buf_ptr] = "y") then compress:=true ; + + +@ We may have to adjust the page dimensions to fit on the internal array. If +the compression mode has been chosen, we must allow extra room for the +uncompressed text. + +@ @<Adjust page dimensions@>= +if dum_width=0 then + if batch_mode then print_width := page_width + else print_width := dfl_screen_width +else print_width := dum_width; +if compress and (print_width > (page_width * 3 div 4)) then + page_width := 4 * print_width div 3 +else if (not compress) and (print_width > page_width - hh_offset) then + page_width := print_width + hh_offset; +if page_width > max_p_width then + begin + page_width := max_p_width; + print_width := page_width - hh_offset; + end; +num_lines := total_rast div page_width; +if dum_height=0 then + if batch_mode then print_height := num_lines + else print_height := dfl_screen_height +else print_height := dum_height; +if print_height > num_lines then print_height := num_lines + + +@ After the dialog is over, we print the options so that the user +can see what \vutex\ thought was specified. This is only done if the +|out_mode| is greater than 0, so we can run quietly usually. + +@<Print all the selected options@>= +if out_mode > 0 then begin +print_ln('Options selected:'); +@.Options selected@> +print(' Starting page = '); +for k:=0 to start_vals do + begin if start_there[k] then print(start_count[k]:1) + else print('*'); + if k<start_vals then print('.') + else print_ln(' '); + end; +print_ln(' Maximum number of pages = ',max_pages:1); +print_ln(' Number of printed characters = ',print_width:1); +print_ln(' Number of printed lines = ',print_height:1); +end + +@ If the user has selected interactive mode, then a summary of the the page +action options is printed for the user's information. + +@<Print the page action options@>= +if not batch_mode then + begin print_ln(' '); + print_ln(' At the end of each printed screen, enter one of:'); + print_ln(' L - left, R - right, U - up, P - new page, <cr> - down'); + print_ln(' '); + end; + +@ Another procedure that is included for debugging and monitoring purposes +is the |diagnostics| procedure. This procedure prints out the memory usage +of \vutex\. Again, this is only done if the |out_mode| is greater than +zero. + +@p procedure diagnostics ; +begin +if out_mode > 0 then begin +print_ln('Number of pages processed: ', actual_page_count:1) ; +print_ln('String pool usage: ',next_names_free:1, ' out of ', name_size:1, + ' bytes') ;end ; +end ; + +@* Defining fonts. +In \vutex, a font definition should only be recognized while the scanning +is not in the postamble, and when the pass is one. + +A global variable |in_postamble| is provided to tell whether we are +processing the postamble or not. + +@<Glob...@>= +@!in_postamble:boolean; {are we reading the postamble?} + +@ @<Set init...@>= +in_postamble:=false; + +@ The following subroutine does the necessary things when a \\{fnt\_def} +command is being processed. + +@p procedure define_font(@!f:integer); {|f| is an external font number} +var +@!p:integer; {length of the area/directory spec} +@!n:integer; {length of the font name proper} +@!c,@!q,@!d:integer; {check sum, scaled size, and design size} +@!k:0..name_size; {indices into |names|} +begin if not in_postamble then begin + if (f>255) then abort('Only font definitions 0..255 are valid in ', + clone) ; +@.vutex capacity exceeded...@> + if (mem[f]<>0) then abort('Font number ',f:1,' already defined!') ; +@.Font already defined@> + cur_fptr := next_mem_free+6 ; + mem[f]:=cur_fptr ; + next_mem_free := next_mem_free + font_desc_size ; +@<Read the font parameters into position for font |nf|, and + print the font name@>; +end ; +end ; + +@ This routine takes the data from the \.{DVI} file about the current font, +and loads it into memory. It also initializes the |use_count| for each +character. + +@<Read the font parameters into position for font |nf|...@>= +c:=signed_quad; font_check_sum:=c;@/ +q:=signed_quad; font_scaled_size := q ; +d:=signed_quad; font_design_size := d ; +font_space:=d div 6;@/ +p:=get_byte; n:=get_byte; +if next_names_free + p + n + 2 > name_size then + abort(clone,' capacity exceeded (name size=',name_size:1,')!'); +@.vutex capacity exceeded...@> +font_nsave := next_names_free * 65536 ; +if out_mode > 0 then print('Font ',f:1,': '); +for k := 1 to p do begin + names[next_names_free] := get_byte ; + next_names_free := next_names_free + 1 ; +end ; +names[next_names_free] := 0 ; +next_names_free := next_names_free + 1 ; +for k := 1 to n do begin + names[next_names_free] := get_byte ; + next_names_free := next_names_free + 1 ; +end ; +names[next_names_free] := 0 ; +next_names_free := next_names_free + 1 ; +cur_char_ptr := cur_fptr ; +for k := 0 to 127 do begin {initialize use counts} + use_count := 0 ; + cur_char_ptr := cur_char_ptr + 2 ; +end ; +font_type := declared ; +if out_mode > 0 then print_font(f) ; + +@ If |p=0|, i.e., if no font directory has been specified, \vutex\ +is supposed to use the default font directory, which is a +system-dependent place where the standard font \.{TFM} files are kept. +@^system dependencies@> + +@d tfm_directory_name=='TeXfonts:' +@d tfm_directory_name_length=9 + +@<Glob...@>= +@!tfm_directory:packed array[1..tfm_directory_name_length] of char; + +@ We stick this initial value into the |names| array for later use. + +@<Set init...@>= +tfm_directory:=tfm_directory_name; + +@ The string |cur_name| is supposed to be set to the external name of the +\.{TFM} file for the current font. +@^system dependencies@> + +@<Move tfm name into the |cur_name| string@>= +begin +for k:=1 to name_length do cur_name[k]:=' '; +r := 1 ; k := font_name ; +if names[k]=0 then + begin for i:=1 to tfm_directory_name_length do + cur_name[i]:=tfm_directory[i]; + r:=tfm_directory_name_length+1; + end else begin + while names[k] > 0 do begin + cur_name[r] := xchr[names[k]] ; + r := r + 1 ; k := k + 1 ; + end ; + end ; +k := k + 1 ; +while names[k] > 0 do begin + cur_name[r] := xchr[names[k]] ; + r := r + 1 ; k := k + 1 ; +end ; +cur_name[r] := '.' ; r := r + 1 ; +cur_name[r] := 'T' ; r := r + 1 ; +cur_name[r] := 'F' ; r := r + 1 ; +cur_name[r] := 'M' ; r := r + 1 ; +for k := 1 to r do + if (cur_name[k]>='a')and(cur_name[k]<='z') then + cur_name[k]:=xchr[xord[cur_name[k]]-@'40] ; +end ; + + +@* Special execution. +In this implementation, special commands are ignored. However, the structure to +handle such remains as a skeleton if some special procedures are desired. + +We need a procedure that will handle any special commands that might appear. +Commands must have the following format: + +\medskip +\line{\hskip20pt command(parameters$\ldots$)\hfil} +\medskip + +If there is more than one command in a special, they must be separated by +spaces. The parameters are also separated by spaces. The string that was +contained in the special command is in the names array starting at +|next_names_free| and ending with a 0. Also, the |prescan| boolean should be +checked to see if this is the first or second time that this string was +encountered. + +@p procedure do_special ; +begin +end ; + +@ This is where the special commands are interpreted. We accumulate the +string, and pass a pointer to it to a routine called |do_special|. Note that +this routine is called twice, once for the prescan, and once for the actual +processing. This way, special commands that set up the device for a particular +mode can be included and processed during the second pass. During pass one, +specials on pages before the first page to be processed are also passed, as +they might contain configuration information. + +@<Translate an |xxx| command@>= +begin +bad_char:=false; +if p+next_names_free > name_size then + abort('Out of string space during special!') ; +@.Out of string space during special@> +for k:=1 to p do + begin q:=get_byte; + if (q<" ")or(q>"~") then + bad_char:=true; + end; +if bad_char then print_ln('non-ASCII character in xxx command!'); +@.non-ASCII character...@> +do_special ; +end ; + +@* Interpreting the page commands. + +The main work of \vutex\ 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. Of course, this is for the second pass. A |prescan| routine +is still necessary to load the font information. + +@ 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 raster analogs of $h$ and $v$, +since it is not always true that |hh=raster_round(h)| or +|vv=line_round(v)|. + +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} +@!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 rasters} + +@ 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; \vutex\ +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} +@!page_count:integer; {the total number of pages seen so far} +@!hpos_ed, @!vpos_ed : boolean ; {are we positioned in this respect?} + +@ @<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): + abort('Can''t call first_par like this.') ; +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; + +@ We need a routine to skip over a font definition. This is very simple, +just requires skipping over the parameters: + +@p procedure skip_font_def ; +var @!dumq, @!dumi, i : integer ; +begin + dumq := signed_quad ; dumq := signed_quad ; dumq := signed_quad ; + dumi := get_byte + get_byte ; + for i := 1 to dumi do dumi := get_byte ; +end ; + +@ A useful constant: + +@<Constants...@>= +one_fourth = 1073741824 ; + +@ Strictly speaking, the |do_page| procedure is really a function with +side effects, not a `\&{procedure}'; it returns the value |false| if +\vutex\ 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 show_state=45 {label for commands that change |s|} +@d change_font=46 {label for commands that change |cur_font|} + +@ Now we have the actual routine that draws a character. It simply sets the +character pointer and calls the routine to set the character in the page. + +@p procedure draw_char(p:integer) ; +@<Character drawing procedures@> +begin cur_char_ptr := cur_fptr + 2*p ; + dev_char_draw ; + hpos_ed := false ; +end ; + +@ Simple horizontal or vertical rules can be created from underscores and +vertical bars. They are given low priority and will be replaced by all other +characters. If a vertical line segment is the only character to appear on a line +in the page, then that line will not be printed. + +@p procedure dev_rule_draw(rh, rw: integer) ; +label done ; +var position : integer ; +i,j : integer ; +begin if line_for[vv] < 0 then begin + line_for[vv] := next_line_free ; + next_line_free := next_line_free + page_width ; + end; + position := line_for[vv] ; j := position + hh + 1 ; + if j < position + page_width then + if rw >= rh then + begin for i := 1 to rw do + begin if (page[j] = xchr[32]) and (j > position) then + page[j] := xchr[95] + else if page[position] = xchr[32] then page[position] := xchr[62] ; + incr(j) ; + if (j >= position + page_width) then + begin if page[position] = xchr[32] then + page[position] := xchr[62] ; + goto done ; + end + end ; + end else if j > position then + begin for i := 1 to rh do + begin if page[j] = xchr[32] then + page[j] := xchr[124] + else page[j-hh-1] := xchr[62] ; + if line_for[vv-i] < 0 then + begin line_for[vv-i] := next_line_free ; + next_line_free := next_line_free + page_width ; + end ; + position := line_for[vv-i] ; + j := position + hh + 1 ; + if j < 0 then goto done ; + end ; + end ; +done: end ; + +@ On entrance to this routine, the character to be drawn will be in |p|, the +font in |cur_fptr|. The routine should be responsible for insuring that the +device is set to the correct location on the page (|hh| and |vv|) and that the +current font is selected. The characters are drawn sequentially into a word +array. + +@<Character drawing procedures@>= +procedure dev_char_draw ; +var position : integer ; {points to beginning of raster row} +i : integer ; +c : char ; {the ASCII character to be set} +begin if not set_word then @<Initialize word@> ; +incr(in_word) ; position := line_for[word_vv] ; +hpos_ed := true ; vpos_ed := true ; +if (hh >= 0 ) then + if (word_hh+in_word < page_width) then + begin i := position+ word_hh + in_word ; + if (i < total_rast) then + if (ord(priority[i]) > font_status) and + (ord(word_priority[in_word]) > font_status) then + begin case font_type of + roman: @<Cases for the roman fonts@> ; + tty: c := xchr[p]; + mitalic: c := mchr[p]; + msymbol: c := schr[p]; + mexten: c := echr[p]; + othercases c := dfl_chr + endcases ; + word[in_word] := c ; + word_priority[in_word] := xchr[font_status] ; + end + else if page[position] = xchr[32] then + page[position] := xchr[62] + else if page[position] = xchr[32] then + page[position] := xchr[62] ; + end else if page[position] = xchr[32] then + page[position] := xchr[62] +else if page[position] = xchr[32] then + page[position] := xchr[62] ; +if font_status > 0 then + if (page[position] = xchr[32]) or (page[position]=xchr[62]) then + page[position] := xchr[42]; +end ; + +@ Variables must be initialized at the beginning of a new word. If the word is +appearing on a new line, the first space in the priority array is used to flag +the line as a baseline or an associated sub/super script line. + +@<Initialize word@>= +begin set_word := true ; +word_hh := hh; word_vv := vv ; +in_word := 0; +if line_for[vv] < 0 then + begin line_for[vv] := next_line_free ; + next_line_free := next_line_free + page_width ; + end ; +if prev_vv >= 0 then + begin if (priority[line_for[vv]]=xchr[126]) then + if prev_vv=vv-1 then + begin page[line_for[vv]] := '-' ; + priority[line_for[vv]] := '-' ; + end + else if prev_vv=vv+1 then + begin page[line_for[vv]] := '+' ; + priority[line_for[vv]] := '+' ; + end + else priority[line_for[vv]] := 'b' + end +else priority[line_for[vv]] := 'b' ; +prev_vv := vv ; +for i := 1 to page_width do + begin word[i] := ' ' ; + word_priority[i] := xchr[126] ; + end ; +end + +@ The layout for the standard roman fonts is slightly different from the ASCII +table. + +@d ff == @'13 {these define the ligatures in roman fonts} +@d fi == @'14 +@d fl == @'15 +@d ffi == @'16 +@d ffl == @'17 +@d ss == @'31 +@d ae == @'32 +@d oe == @'33 +@d AE == @'35 +@d OE == @'36 + +@<Cases for the roman fonts@>= +case p of + ff: begin word[in_word] := rchr[102] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[102] ; end ; + fi: begin word[in_word] := rchr[102] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[105] ; end ; + fl: begin word[in_word] := rchr[102] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[108] ; end ; + ffi: begin word[in_word] := rchr[102] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; word[in_word] := rchr[102] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[105] ; end ; + ffl: begin word[in_word] := rchr[102] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; word[in_word] := rchr[102] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[108] ; end ; + ss: begin word[in_word] := rchr[115] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[115] ; end ; + ae: begin word[in_word] := rchr[97] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[101] ; end ; + oe: begin word[in_word] := rchr[111] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[101] ; end ; + AE: begin word[in_word] := rchr[65] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[69] ; end ; + OE: begin word[in_word] := rchr[79] ; + word_priority[in_word] := xchr[font_status] ; + incr(in_word) ; c := rchr[69] ; end ; + othercases c:= rchr[p] + endcases + +@ We need the global page arrays + +@<Glob...@>= +@!page : array [0..total_rast] of text_char ; {the page of data} +@!priority : array [0..total_rast] of text_char ; {priority array} +@!line_for : array [1..dfl_n_lines] of integer ; {points to line in array} +@!next_line_free : integer ; {points to the next free position in array} +@!word : array [0..max_p_width] of text_char ; {string to hold complete word} +@!word_priority : array [0..max_p_width] of text_char ; {priority of each char} +@!in_word : integer ; {points to location in word} +@!set_word : boolean ; {set to true if a word has begun} +@!word_hh : integer ; {hh value where word began} +@!word_vv : integer ; {vv value of first character in word} +@!min_hh,@!min_vv : integer ; {minimum hh, vv values on the page} +@!prev_vv: integer ; + +@ This routine will reset the characters in a word if the positioning has +left holes or missing characters. + +@p procedure reset_word ; +var last:integer; {position of last character in word to be reset} +i,j,position : integer ; +begin set_line(vv) ; +if word_hh < min_hh then min_hh := max(0,word_hh) ; +if word_vv < min_vv then min_vv := max(0,word_vv) ; +if hh > word_hh then + begin position := line_for[word_vv] ; + j := position + word_hh + 1 ; + last := hh - word_hh + 1 ; if last > in_word then last := in_word ; + for i := 1 to last do + begin if j > position then + if priority[j] > word_priority[i] then + begin page[j] := word[i] ; + priority[j] := word_priority[i] ; + end + else if page[position] = xchr[32] then + page[position] := xchr[62] + else if page[position] = xchr[32] then + page[position] := xchr[62] ; + incr(j) ; + end ; + end ; + set_word := false ; +end ; + +@ This routine is called at the beginning of each page, before anything +is processed. + +@p procedure start_page ; +var i : integer ; +begin + hpos_ed := false ; vpos_ed := false ; + for i := 0 to total_rast do + begin page[i] := xchr[32]; + priority[i] := xchr[126] ; + end; + for i := 1 to num_lines do line_for[i] := -1; + next_line_free := 0; + min_hh :=hh_offset ; min_vv := vv_offset ; + prev_vv := -1 ; +end ; + +@ These constants define the key strokes for interactive paging action. + +@<Constants...@>= +@!u_left = "L"; l_left="l"; {key for move left on page} +@!u_right= "R"; l_right="r"; {key for move right on page} +@!u_up= "U"; l_up="u"; {key for move up on page} +@!u_page= "P"; l_page="p"; {key for move to next page} +@!u_comp= "C"; l_comp="c"; {key for compress text on page} + +@ When this routine is called, a full page has been processed, and the +page array is sent to the output device. + +@p procedure finish_page ; +label done; +var i,j,k: integer ; {index counters} + last_hh: integer ; {raster column of furthest right character} + last_vv: integer ; {raster row of furthest down character} + first_p_hh: integer ; {raster column of first printed character} + last_p_hh: integer ; {raster column of last printed character} + first_p_row: integer ; {row number of first printed row} + last_p_row: integer ; {row number of last printed row} + rows_printed: integer ; + raster: integer ; + jspace: integer ; + shift: integer ; + next_row: integer ; + position: integer ; + average: integer; {average spacing between baselines} + menu : integer ; {used to interactively select page-action} + printing_page : boolean ; +begin first_p_hh := min_hh ; first_p_row := min_vv ; +last_hh := raster_round(max_h_so_far) + 1 ; +last_vv := line_round(max_v_so_far) ; +if last_hh >= page_width then + last_hh := page_width - 1 ; +@<Determine average spacing between nonempty lines@> ; +if compress then @<Compress spaces between words@> ; +printing_page := true; +position := first_p_row; +while (printing_page) do + begin last_p_hh := first_p_hh + print_width-2; + if last_p_hh >= page_width then last_p_hh := page_width; + write_ln(bit_file); + i := first_p_row; + rows_printed := 0; + while (i <= last_vv) and (rows_printed < print_height) do + begin if line_for[i] >= 0 then + begin position := i ; + raster := line_for[i] ; + if raster > total_rast-last_hh-1 then goto done ; + write(bit_file,page[raster]) ; + k := raster + last_p_hh ; + while page[k] = xchr[32] do decr(k) ; + k := k - raster - 1 ; + raster := raster + first_p_hh ; + for j := first_p_hh to k do + begin write(bit_file,page[raster]) ; + incr(raster) ; + end ; + write_ln(bit_file,page[raster]) ; + incr(rows_printed) ; + end + else if (average >0) and (i-position > average) then + begin write_ln(bit_file) ; + position := i ; incr(rows_printed) ; + end ; + incr(i); + end; + done: @<Determine page action@> ; + end; +end ; + +@ Since each physical line is represented by perhaps several lines in the page +arrays, we try to determine the spacing between physical lines by taking the +average number of blank lines in the array between nonempty lines. This is used +to approximate the number of blank lines in a vertical shift in \TeX. +Note that a page number at the bottom of a sparse page may distort the average. +Hence if the last line is set apart substantially, we will ignore it. + +@<Determine average spacing between nonempty lines@>= +begin + average := 0 ; k:= 0 ; + i := 1 ; j := line_for[i] ; + while (i <= last_vv) and ((j < 0) or (priority[j] <>'b')) do + begin incr(i); j:= line_for[i]; + end; + position := i ; + incr(i) ; + while i <= last_vv do + begin j := line_for[i] ; + if (j>=0) then + if (priority[j]='b') then + begin shift := i - position ; + average := average + shift ; + incr(k) ; position := i ; + end; + incr(i); + end ; + if k > 1 then + begin if shift > 1.1*round(average*1.0/k) then + begin k := k - 1 ; + average := average - shift ; + end; + if k > 1 then + average := round(average*1.0 / k ) + end; +end + +@ If the compress option is chosen, the text is compressed vertically so that +associated sub/super script lines are pushed into the baseline. Then the +horizontal spacing is abandonned and interword spacing is compressed to one +blank space. + +@<Compress spaces between words@>= +begin +for i := min_vv to last_vv do + if line_for[i]>=0 then + if priority[line_for[i]]='+' then begin + raster := line_for[i] ; + position := line_for[i+1] ; + for j := min_hh to last_hh do + if (page[raster+j]<>xchr[32]) then + if (page[position+j]=xchr[32]) or + (priority[raster+j]<priority[position+j]) then + begin + page[position+j] := page[raster+j] ; + priority[position+j] := priority[raster+j]; + end; + + line_for[i] := -2 + end ; +for i := last_vv downto min_vv do + if line_for[i]>=0 then + if priority[line_for[i]]='-' then begin + raster := line_for[i] ; + position := line_for[i-1] ; + for j := min_hh to last_hh do + if (page[raster+j]<>xchr[32]) then + if (page[position+j]=xchr[32]) or + (priority[raster+j]<priority[position+j]) then + begin + page[position+j] := page[raster+j] ; + priority[position+j] := priority[raster+j]; + end; + line_for[i] := -2 + end ; +for i := min_vv to last_vv do + if line_for[i]>=0 then + begin raster := line_for[i] + 1 ; + next_row := raster + page_width - 1; + while (page[raster] = ' ') and (raster < next_row) do + incr(raster) ; + while (page[raster] <> ' ') and (raster < next_row) do + incr(raster) ; + jspace := raster ; + while raster < next_row do + begin + while (page[raster] = ' ') and (raster < next_row) do + incr(raster) ; + shift := raster - jspace - 1 ; + while (page[raster] <> ' ') and (raster < next_row) do + begin page[raster-shift] := page[raster] ; + incr(raster) ; + end ; + for k := raster - shift to raster - 1 do page[k] := ' ' ; + jspace := raster - shift ; + end ; + end ; +last_hh := min(last_hh,jspace); +end + +@ When the portion of the page is printed, either the interactive user will be +given a menu of options to chose from or the batch job will continue. + +@d null = 0 +@d left_arrow = 1 +@d right_arrow = 2 +@d up_arrow = 3 +@d down_arrow = 4 +@d comp = 5 +@d next_page = 6 + +@<Determine page action@> = +begin if batch_mode then + @<Draw graded horizontal rule@> +else begin last_p_row := i-1 ; + @<Select from menu@>; + case menu of + left_arrow: begin first_p_hh := first_p_hh - print_width + hor_overlap; + if first_p_hh < min_hh then first_p_hh := min_hh; + end; + right_arrow: begin first_p_hh := last_p_hh - hor_overlap; + if first_p_hh > last_hh - print_width then + first_p_hh := last_hh - print_width ; + end; + up_arrow: begin k := 0; + while (k < print_height-vert_overlap) and + (first_p_row > min_vv) do + begin first_p_row := first_p_row - 1 ; + j := line_for[first_p_row] ; + if (j >= 0) and (priority[j] ='b') then incr(k) ; + end; + end; + down_arrow: if last_p_row = last_vv then + printing_page := false + else + begin first_p_row := last_p_row - vert_overlap; + if first_p_row > last_vv - print_height then + first_p_row := last_vv - print_height + 1; + end; + comp: if (not compress) then @<Compress spaces between words@>; + next_page: @<Draw graded horizontal rule@>; + othercases @<Draw graded horizontal rule@> + endcases + end; +end + +@ The interactive user will be given a choice of re-drawing the page, shifted +right, left, up, or down, or in the compressed mode. Any other selection will +cause the process to go to the next page. + +@ @<Select from menu@> = +begin rewrite(term_out); write(term_out,'Hit key for page action.. '); +input_ln; +buf_ptr :=0; +menu := null; +if (buffer[buf_ptr] = u_left) or (buffer[buf_ptr] = l_left) then + menu := left_arrow +else if (buffer[buf_ptr] = u_right) or (buffer[buf_ptr] = l_right) then + menu := right_arrow +else if (buffer[buf_ptr] = u_up) or (buffer[buf_ptr] = l_up) then + menu := up_arrow +else if (buffer[buf_ptr] = u_page) or (buffer[buf_ptr] = l_page) then + menu := next_page +else if (buffer[buf_ptr] = u_comp) or (buffer[buf_ptr] = l_comp) then + menu := comp +else menu := down_arrow; +incr(buf_ptr); +end + +@ At the end of each page, a graded horizontal rule will be drawn. The +gradations will be every 2 cm. measured from point with dvi value 0. + +@<Draw graded horizontal rule@> = +begin write_ln(bit_file); +position := trunc(200000.0/num*den/resol) ; +write(bit_file,' _ cm --> '); +i := 0; +while i <= last_p_hh do + begin + if i > first_p_hh + 9 then + if i mod position = 0 then + begin + if 2*(i div position) < 10 then + write(bit_file,(2*(i div position)):1,'_') + else + write(bit_file,(2*(i div position)):2); + incr (i); + end + else write(bit_file,'_') ; + incr (i); + end; +write_ln(bit_file); +printing_page := false; +end + + +@ Some \PASCAL\ compilers severely restrict the length of procedure bodies, +so we shall split |do_page| into two parts, one of which is +called |spcl_cases|. The different parts communicate with each other +via the global variables mentioned above, together with the following ones: + +@<Glob...@>= +@!s:integer; {current stack size} +@!cur_font:integer; {current internal font number} +@!a:integer; {byte number of the current command} + +@ Here is the overall setup. + +@p @t\4@>@<Declare the function called |spcl_cases|@>@; +function do_page:boolean; +label fin_set,fin_rule,move_right,show_state,done,9998,9999; +var o:eight_bits; {operation code of the current command} +@!p,@!q:integer; {parameters of the current command} +@!hhh:integer; {|h|, rounded to the nearest pixel} +begin start_page; cur_font:=0; {set current font undefined} +while mem[cur_font]>0 do cur_font := cur_font + 1 ; +s:=0; h:=0; v:=0; w:=0; x:=0; y:=0; z:=0; +hh:=raster_round(0); vv:=line_round(0); + {initialize the state variables} +cur_fptr := 0 ; set_word := false ; +while true do @<Translate the next command in the \.{DVI} file; + |goto 9999| with |do_page=true| if it was |eop|; + |goto 9998| if premature termination is needed@>; +9998: print_ln('!'); do_page:=false; +9999: end; + +@ @<Translate the next command...@>= +begin a:=cur_loc ; +o:=get_byte; if o > 127 then p:=first_par(o); +if eof(dvi_file) then bad_dvi('the file ended prematurely'); +@.the file ended prematurely@> +@<Start translation of command |o| and |goto| the appropriate label to + finish the job@>; +fin_set: @<Finish a command that either sets or puts a character, then + |goto move_right| or |done|@>; +fin_rule: @<Finish a command that either sets or puts a rule, then + |goto move_right| or |done|@>; +move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>; +show_state: @<Show the values of |h|, |v|, |w|, |x|, |y|, |z|, + |hh|, and |vv|; 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 draw_char(o) +else case o of + four_cases(set1), four_cases(put1): begin draw_char(o); goto fin_set; + end; + set_rule, put_rule: begin q:=signed_quad ; dev_rule_draw(round(p/vresol), + round(q/resol)); goto fin_rule; + end; + @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@; + @t\4@>@<Cases for horizontal motion@>@; + othercases if spcl_cases(o,p) then goto done@+else goto 9998 +endcases ; + +@ @<Declare the function called |spcl_cases|@>= +function spcl_cases(@!o:eight_bits;@!p:integer):boolean; +label change_font,move_down,done,9998; +var q:integer; {parameter of the current command} +@!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 raster} +begin pure:=true; +case o of +@t\4@>@<Cases for vertical motion@>@; +@t\4@>@<Cases for fonts@>@; +four_cases(xxx1): begin @<Translate an |xxx| command@>; goto done; end; +pre: begin print_ln('preamble command within a page!'); goto 9998; + end; +@.preamble command within a page@> +post,post_post: begin print_ln('postamble command within a page!'); goto 9998; +@.postamble command within a page@> + end; +othercases begin print_ln('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: spcl_cases:=pure; +end; + +@ @<Cases for commands |nop|, |bop|, \dots, |pop|@>= +nop: goto done; +bop: begin print_ln('bop occurred before eop'); goto 9998; +@.bop occurred before eop@> + end; +eop: begin finish_page ; + if s<>0 then print_ln('stack not empty at end of page (level ', + s:1,')!'); +@.stack not empty...@> + do_page:=true; goto 9999; + end; +push: begin + if s=max_s_so_far then + begin max_s_so_far:=s+1; + if s=max_s then print_ln('deeper than claimed in postamble!'); +@.deeper than claimed...@> +@.push deeper than claimed...@> + if s=stack_size then + begin print_ln(clone, ' capacity exceeded (stack size=', + stack_size:1,')'); goto 9998; + end; + 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); goto show_state; + end; +pop: begin + if s=0 then print_ln('(illegal at level zero)!') + else begin if set_word then reset_word ; + 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; + goto show_state; + end; + +@ Our screen resolution is coarse, and we shall reset characters in words so +that the characters are left justified in the space \TeX\ provides. We shall do +our position calculations in the same manner that they would be done on the +printer. Rounding to the nearest raster 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. + +Note that, if the horizontal movement is large, the word is reset. + +@d out_space(#)==if cur_fptr=0 then hh := raster_round(h+p) + else begin + if (abs(p) >= round(vresol / 3)) and (set_word) then reset_word ; + if (p>=font_space)or(p<=-4*font_space) then + hh:=raster_round(h+p) + else hh:=hh+round(p/resol); + end; + q:=p; hpos_ed := false ; goto move_right + +@<Cases for horizontal motion@>= +four_cases(right1):begin out_space('right',o-right1+1:1); + end; +w0,four_cases(w1):begin w:=p; out_space('w',o-w0:1); + end; +x0,four_cases(x1):begin x:=p; out_space('x',o-x0:1); + 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. + +Again, if the the vertical movement is large, the word is reset. + +@d out_vmove(#)==if cur_fptr=0 then vv := line_round(v+p) + else begin + if (p <> 0) and (set_word) then reset_word ; + if abs(p)>=5*font_space then + vv:=line_round(v+p) + else vv:=vv+round(p/vresol); + end; + vpos_ed := false ; + goto move_down + +@<Cases for vertical motion@>= +four_cases(down1):begin out_vmove('down',o-down1+1:1); + end; +y0,four_cases(y1):begin y:=p; out_vmove('y',o-y0:1); + end; +z0,four_cases(z1):begin z:=p; out_vmove('z',o-z0:1); + end; + +@ @<Cases for fonts@>= +sixty_four_cases(fnt_num_0), four_cases(fnt1): goto change_font; +four_cases(fnt_def1): begin + skip_font_def; + goto done; + end; + +@ @<Finish a command that either sets or puts a character...@>= +q:=tfm_width ; +if o>=put1 then goto done; +hh:=hh+raster_width; +goto move_right + +@ @<Finish a command that either sets or puts a rule...@>= +if o=put_rule then goto done; +hh:=hh+round(q/resol); goto move_right + +@ A sequence of consecutive rules, or consecutive characters in a fixed-width +font whose width is not an integer number of rasters, can cause |hh| to drift +far away from a correctly rounded value. \vutex\ ensures that the +amount of drift will never exceed |max_drift| rasters. + +@d infinity==@'17777777777 {$\infty$ (approximately)} +@d max_drift=1 {we insist that abs|(hh-raster_round(h))<=max_drift|} + +@<Finish a command that sets |h:=h+q|, then |goto done|@>= +hhh:=raster_round(h+q); +if abs(hhh-hh)>max_drift then begin hpos_ed := false ; + 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)>max_h+99 then + begin print_ln('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 print_ln('arithmetic overflow! parameter changed from ', +@.arithmetic overflow...@> + p:1,' to ',infinity-v:1); + p:=infinity-v; + end; +if (v<0)and(p<0) then if -v>p+infinity then + begin print_ln('arithmetic overflow! parameter changed from ', + p:1, ' to ',(-v)-infinity:1); + p:=(-v)-infinity; + end; +vvv:=line_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)>max_v+99 then + begin print_ln('warning: |v|>',max_v:1,'!'); +@.warning: |v|...@> + max_v:=abs(v); + end; + max_v_so_far:=abs(v); + end; +goto done + +@ @<Show the values of |h|, |v|, |w|, |x|, |y|, |z|...@>= +goto done + +@ @<Finish a command that changes the current font...@>= +cur_fptr := mem[p] ; +goto done + +@* Skipping pages. +A routine that's much simpler than |do_page| is used to pass over +pages that are not being translated. The |skip_pages| subroutine +is assumed to begin just after the preamble has been read, or just +after a |bop| has been processed. It continues until either finding a +|bop| that matches the desired starting page specifications, or until +running into the postamble. + +@p procedure skip_pages; +label 9999; {end of this subroutine} +var p:integer; {a parameter} +@!k:0..255; {command code} +@!down_the_drain:integer; {garbage} +bad_char:boolean ; +begin while true do + begin if eof(dvi_file) then bad_dvi('the file ended prematurely'); +@.the file ended prematurely@> + k:=get_byte; + if k > 127 then begin p:=first_par(k); + case k of + bop: begin @<Pass a |bop| command, setting up the |count| array@>; + if not started and start_match then + begin started:=true; goto 9999; + end; + end; + set_rule,put_rule: down_the_drain:=signed_quad; + fnt_def1,fnt_def1+1,fnt_def1+2,fnt_def1+3: if prescan then begin + define_font(p); + end else skip_font_def ; + xxx1,xxx1+1,xxx1+2,xxx1+3: if prescan then begin + @<Translate an |xxx| command@> + end else + while p>0 do + begin down_the_drain:=get_byte; decr(p); + end; + post: begin in_postamble:=true; goto 9999; + end; + othercases do_nothing + endcases; + end ; + end; +9999:end; + +@ Now we need a routine that will scan a page from the |bop| to the |eop|, +maintaining information about font usage. This routine also catches errors +like illegal font numbers or characters. It also counts the number of pages +actually processed. + +@p function scan_page : boolean ; +label 9999; {end of this subroutine} +var p:integer; {a parameter} +@!k:0..255; {command code} + bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?} +@!down_the_drain:integer; {garbage} +begin actual_page_count := actual_page_count + 1 ; + while true do + begin a:=cur_loc ; + if eof(dvi_file) then bad_dvi('the file ended prematurely'); +@.the file ended prematurely@> + k:=get_byte; + if eof(dvi_file) then bad_dvi('the file ended prematurely'); +@.the file ended prematurely@> + if k<set_char_0+128 then begin + cur_char_ptr := cur_fptr + 2*k ; + incr(use_count); + end else begin + p:=first_par(k); + case k of + bop: begin @<Pass a |bop| command, setting up the |count| array@>; + if not started and start_match then + begin started:=true; goto 9999; + end; + end; + eop: goto 9999 ; + four_cases(set1),four_cases(put1): begin if p > 128 then + abort('character greater than 128 encountered!') +@.character greater than...@> + else begin cur_char_ptr := cur_fptr + 2*p ; incr(use_count); end; end ; + set_rule,put_rule: down_the_drain:=signed_quad; + fnt_def1,fnt_def1+1,fnt_def1+2,fnt_def1+3: define_font(p); + sixty_four_cases(fnt_num_0),four_cases(fnt1): begin + if p>255 then abort(clone, ' cannot handle font numbers > 255!') ; +@.vutex capacity exceeded...@> + cur_fptr := mem[p] ; + if cur_fptr=0 then abort('Undefined font!') ; +@.Undefined font@> + end ; + four_cases(xxx1): @<Translate an |xxx| command@> + post: begin in_postamble:=true; goto 9999; + end; + othercases do_nothing + endcases; + end ; + end; +9999:scan_page := true; end; + +@ The global variable |started| indicates that we have found the +starting page. |last_page| points to the most recent page processed, +and |prev_page| points to the page before it. These two globals can be +used for processing a file in the reverse output, if the printer being +driven stacks the pages backwards, for instance. + +@<Glob...@>= +@!started:boolean; {has the starting page been found?} +@!last_page: integer; {what is the most recent page processed?} +@!prev_page: integer; {what is the page before it?} +@!actual_page_count: integer; {how many pages to actually process} + +@ @<Set init...@>= +started:=false; +last_page:=-1; +prev_page:=-1; +actual_page_count:=0; + +@ @<Pass a |bop|...@>= +last_page:=cur_loc; +incr(page_count); +for k:=0 to 9 do count[k]:=signed_quad; +prev_page:=signed_quad ; {skip over the back pointers} + +@ We need a procedure to open the output file, and to send out any +initialization that the printer might need. + +@<Open bit file and initialize device@>= +open_bit_file ; init_device ; + +@* Page scanning routines. +Here are a few miscellaneous routines which will scan the page, calling +the |pre_scan| and |do_page| routines as necessary. + +@<Process the preamble@>= +open_dvi_file; +p:=get_byte; {fetch the first byte} +if p<>pre then bad_dvi('First byte isn''t start of preamble!'); +@.First byte isn't...@> +p:=get_byte; {fetch the identification byte} +if p<>id_byte then + print_ln('identification in byte 1 should be ',id_byte:1,'!'); +@.identification...should be n@> +@<Read the conversion units@>; +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('''') + +@ We also need a routine to skip over the preamble after the file has been +reset again. + +@<Skip over the preamble@>= +reopen_dvi_file; +p:=get_byte; {fetch the first byte} +p:=get_byte; {fetch the identification byte} +p:=signed_quad; p:=signed_quad; p:=signed_quad; {skip over the mag stuff} +p:=get_byte; {fetch the length of the introductory comment} +while p>0 do begin + i:=get_byte; decr(p); end; + +@ Read in the conversion units and magnification for the font. + +@<Glob...@>= +@!num,den : integer; {numerator and denominator for conversion of dvi units} + +@ @<Read the conversion units@>= +num:=signed_quad; if num<=0 then bad_dvi('numerator is ',num:1); +@.numerator is wrong@> +den:=signed_quad; if den<=0 then bad_dvi('denominator is ',den:1); +@.denominator is wrong@> +p:=signed_quad; + +@ 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 up to |max_pages| pages@>= +begin print('- Page '); + while max_pages>0 do + begin decr(max_pages); + for k:=0 to start_vals do + begin print('[',count[k]:1); + if k<start_vals then print('.') + else print ('] '); + end ; + if not do_page then bad_dvi('page ended unexpectedly'); +@.page ended unexpectedly@> + begin + repeat k:=get_byte; + if (k>=fnt_def1)and(k<fnt_def1+4) then + begin p:=first_par(k); define_font(p); k:=nop; + end; + until k<>nop; + if k=post then + begin in_postamble:=true; goto done; + end; + if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop'); +@.byte n is not bop@> + @<Pass a |bop|...@>; + end ; + end; +done: end; + +@ This routine will prescan the file, using the |scan_page| macro to get +the information out of each page. + +@<Prescan up to |max_pages| pages@>= +begin while max_pages>0 do + begin decr(max_pages); + if not scan_page then bad_dvi('page ended unexpectedly'); +@.page ended unexpectedly@> + if in_postamble then goto pdone ; + repeat k:=get_byte; + if (k>=fnt_def1)and(k<fnt_def1+4) then + begin p:=first_par(k); skip_font_def; k:=nop; + end; + until k<>nop; + if k=post then + begin in_postamble:=true; goto pdone; + end; + if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop',k); +@.byte n is not bop@> + @<Pass a |bop|...@>; + end; +pdone:end + +@* Font loading. +Now we have the procedure which determines the vertical and +horizontal raster resolution. On entrance to this procedure, all the font usage +information will have been loaded into the |use_count| entries for each font +descriptor. Based on this, |base_font| will determine the resolution to use +for assigning screen raster positions. + +First, this routine reads the file \.{nonASCII.tex.fnt} to determine which fonts +are non ASCII, i.e. not printable on the screen. This list is shorter than the +list of ASCII fonts. + +@p procedure base_font ; +var i, j, k : integer ; + f_type : integer ; {type for listed fonts} + lf,lh,bc,ec,nw,np : integer ; {help in decoding the \.{TFM} file} + value, x : integer ; {a \.{TFM} |fix_word|} +@<|base_font| variables@> +begin + @<Load non ASCII font information@> ; + @<Determine base font@> ; +end ; + +@ The non ASCII font information is assumed to be in the following format: +\medskip +{\def\ff#1\cr{\line{\hskip\parindent\tt #1\hfil}} +\ff amsy 2\cr +\ff ambsy 2\cr +\ff amcsc 2\cr +\ff amit 4\cr +\ff amtt 5\cr +\medskip + +The only item on a line is the font name and the type code. + +@d font_list_file=='nonASCII.tex.fnt' {change this to correct name} +@d list_len==16 + +@<Glob...@>= +@!resol, vresol : real; {number of dvi units per raster or line} +@!gen_input : text_file ; {input path for text} +@!list_fonts : packed array [1..list_len] of char ; {non ASCII fonts name} +@!num_asc_fonts : integer ; {number of ASCII fonts declared} + +@ @<Set init...@>= +list_fonts := font_list_file ; + +@ @<Load non ASCII font information@>= +for k := 1 to name_length do cur_name[k] := ' ' ; +for k := 1 to list_len do + if (list_fonts[k] <= 'z') and (list_fonts[k] >= 'a') then + cur_name[k] := xchr[xord[list_fonts[k]]-@'40] + else cur_name[k] := list_fonts[k] ; +open_input_text ; +num_asc_fonts := 0 ; +while not eof(gen_input) do + @<Process line@> ; + +@ At this point, we are at the beginning of a line in the non ASCII font +information file. We read in the name of a non printable font, and then scan +through our fonts looking for a match. By default all fonts are assumed to be +roman unless named in this file. + +@<Process line@>= +begin + buf_ptr := 1 ; + while not eoln(gen_input) do begin + buffer[buf_ptr] := xord[gen_input^] ; + get(gen_input) ; + buf_ptr := buf_ptr + 1 ; + end ; + read_ln (gen_input) ; + buffer[buf_ptr] := " " ; + buf_ptr := 1 ; + while buffer[buf_ptr] <> " " do buf_ptr := buf_ptr + 1 ; + while buffer[buf_ptr] = " " do buf_ptr := buf_ptr + 1 ; + f_type := get_integer ; + for i := 0 to 255 do if mem[i] > 0 then begin + cur_fptr := mem[i] ; + j := font_name ; k := 1 ; + if names[j] = 0 then begin + if (font_type)=declared then + begin font_type := roman ; + incr(num_asc_fonts) ; + end ; + j := j + 1 ; + while (names[j] = buffer[k]) do begin + j := j + 1 ; k := k + 1 ; end ; + if (buffer[k] = " ") then begin + font_type := f_type ; + if f_type = other then decr(num_asc_fonts) ; + end ; + end ; + end ; +end + +@ This section of code figures out which font is to determine the resolution. +It goes through the list of fonts, and, for each font that is ASCII, it +calculates the |use_count| and the font number into a list built at the top of +memory. This list is then sorted, and the fonts are given priorities. +Horizontal resolution normally is slightly less than the width of normal +interword space, except for fonts where this parameter is 0 in which case one +third of a quad is used. + +The vertical resolution is determined by placing |num_lines| on the physical +page. + +@d dd_s_k(#)==mem[temp_ar(#)-1] {sort key for advantages} + +@<Determine base font@>= +@<Make list of declared fonts and calculate advantages@> ; +@<Sort by advantages and build linked list@> ; +@<Load the font data@> ; + +@ Making a list of the declared fonts is relatively easy, as is calculating +the advantages. The advantage is the use count for all fonts. + +@<Make list of declared fonts...@>= +asc_num := 0 ; +nasc_num := num_asc_fonts ; +for i := 0 to 255 do if mem[i] > 0 then begin + cur_fptr := mem[i] ; + cur_char_ptr := cur_fptr ; + dd_total := 0 ; + for j := 0 to 127 do begin + if use_count > 0 then + dd_total := dd_total + use_count ; + cur_char_ptr := cur_char_ptr + 2 ; + end ; + if (dd_total >0) then + begin if (font_type > other) then + begin temp_ar(asc_num) := cur_fptr ; + incr(asc_num) ; + end else if (font_type = other) then + begin temp_ar(nasc_num) := cur_fptr ; + incr(nasc_num) ; + end ; + end ; + mem[cur_fptr + 1] := dd_total ; +end + +@ The sort to determine the priority of the fonts is a simple insertion sort. +Since this sort is only performed once, and it usually has less than a dozen +elements to sort, simplicity is called for. Note that the ASCII fonts are +sorted at the top, and the non-ASCII fonts are sorted last. + +@<Sort by advantages and build linked list@>= +for i := asc_num-1 downto 1 do + for j := 0 to i-1 do + if mem[temp_ar(j)+1] < mem[temp_ar(i)+1] then begin + t := temp_ar(i) ; temp_ar(i) := temp_ar(j) ; temp_ar(j) := t ; + end ; +for i := nasc_num-1 downto num_asc_fonts+1 do + for j := num_asc_fonts to i-1 do + if mem[temp_ar(j)+1] < mem[temp_ar(i)+1] then begin + t := temp_ar(i) ; temp_ar(i) := temp_ar(j) ; temp_ar(j) := t ; + end ; +for j := 0 to asc_num-1 do dd_s_k(j) := j ; +for j := num_asc_fonts to nasc_num-1 do dd_s_k(j) := j ; + +@ Some of |base_font|' locals: + +@<|base_font| variables@>= +@!asc_num : integer ; {positions the ASCII fonts used} +@!nasc_num : integer ; {positions the non-ASCII fonts used} +@!t : integer ; {temporary holding place for exchange swaps} +@!dd_total : integer ; {contains total number of different characters} + +@ We now will load the \.{TFM} file for each font. We also will determine the +resolution from the |base_font| and will scale all \.{TFM} widths accordingly. + +@<Load the font data@>= +cur_fptr := mem[next_mem_free] ; +load_tfm_file ; @<Determine vertical and horizontal resolution@> ; +for i := 0 to 255 do + begin if (mem[i] > 0) then + begin cur_fptr := mem[i] ; + if font_status <> 0 then load_tfm_file ; + cur_char_ptr := cur_fptr ; + for j := 0 to 127 do begin + if (use_count > 0) then begin + raster_width := round(tfm_width/resol) ; + if raster_width < 1 then raster_width := 1; + end ; + cur_char_ptr := cur_char_ptr + 2 ; + end ; + end ; +end + +@ The vertical resolution is determined to place |num_lines| on a page of +dimension |page_length|. The horizontal resolution is given by the inter-word +space for the |base_font|. Note that this section assumes that the \.{TFM} data +for the |base_font| is exactly as it was initally read into |mem|. + +@<Determine vertical and horizontal resolution@>= +begin lf := hi(mem[next_mem_free]) ; lh := lo(mem[next_mem_free]) ; + bc := hi(mem[next_mem_free+1]) ; ec := lo(mem[next_mem_free+1]) ; + nw := hi(mem[next_mem_free+2]) ; np := lo(mem[next_mem_free+5]) ; + k := next_mem_free + 6 + lh - bc ; + vresol := den / (num / 100000.0) * page_length / num_lines ; + value := mem[next_mem_free + lf - np + 1] ; + x := tfm_to_int(value) ; + resol := x * 1.0 ; + if resol <= 0 then + begin value := mem[next_mem_free + lf - np + 5] ; + x := tfm_to_int(value) ; + resol := x / 3.0 ; + end; +end ; + +@ This routine may be used to initialize the output device to begin receiving +information. This section could be personalized to a particular system. + +@p procedure init_device ; +begin +end ; + +@ Now for the antithesis of the above. This routine cleans up the \TeX\ +job, and prepares for the next. + +@p procedure clean_printer ; +begin + write_ln(bit_file); + write_ln(bit_file,'The end...'); + write_ln; +end ; + +@* The main program. +Now we are ready to put it all together. This is where \vutex\ starts, +and where it ends. + +@p begin initialize; {get all variables initialized} +dialog; {set up all the options} +prescan := true ; +@<Process the preamble@>; +skip_pages; +if not in_postamble then + @<Prescan up to |max_pages| pages@>; +@<Open bit file and initialize device@>; +base_font ; +prescan:=false ; +in_postamble := false ; +started:=false ; +max_pages:=actual_page_count ; +@<Skip over the preamble@>; +skip_pages; +@<Translate up to |max_pages| pages@>; +final_end: +if bit_is_open then clean_printer; +diagnostics; +end. + +@ The main program needs a few global variables in order to do its work. + +@<Glob...@>= +@!k,@!p,@!q:integer; {general purpose registers} +@!prescan:boolean; {indicates that we are in prescan phase} +@!bit_is_open : boolean ; {indicates that output file was opened.} + +@ @<Set init...@>= +bit_is_open := false ; + +@* System-dependent changes. +This section should be replaced, if necessary, by changes to the program +that are necessary to make \vutex\ work at a particular installation. +Any additional routines should be inserted here. +@^system dependencies@> + +@* Index. +Pointers to error messages appear here together with the section numbers +where each ident\-i\-fier is used. + |