[mod.sources] v06i014: TeX DVI driver for LaserJet+

sources-request@mirror.UUCP (06/22/86)

Submitted by: Tor Lillqvist <talcott!seismo!mcvax!santra!tml>
Mod.sources: Volume 6, Issue 14
Archive-name: texdvi2lj/Part2

[ I have split this submission into three pieces named LJ.1.3,
  LJ.2.3, and LJ.2.3.  Unpack each piece and do
	  "cat LJ.[123].3 >dvi2lj.web"
  This is not great, but is the best way I could think of to
  distribute a large single-file source; if you have a better
  way, please let me know.
	*	*	*	*	*	*
  OOPS!  This article went out earlier with a wrong subject
  line.  I sent out a cancel, but in case it didn't get out,
  here's the "official" posting.
	*	*	*	*	*	*
		--r$]

Here is a DVI (TeX output) driver for the HP LaserJet+.  This version
is for the Pascal/1000 compiler on HP1000 machines running RTE-A (and
my TeX implementation), but it should be fairly easy to convert to
other TeX implementations, compilers and operating systems.

DVIplus is based on the DVItype program. It downloads only those
characters actually used. Pages are printed in reverse.

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# Contents:  LJ.2.3
 
echo x - LJ.2.3
sed 's/^XX//' > "LJ.2.3" <<'@//E*O*F LJ.2.3//'
XX@* Reading the font information.
XXThe current number of known fonts is |nf|. Each known font has
XXan internal number |f|, where |0<=f<nf|; the external number of this font,
XXi.e., its font identification number in the \.{DVI} file, is
XX|font_num[f]|, and the external name of this font is the string that
XXoccupies positions |font_name[f]| through |font_name[f+1]-1| of the array
XX|names|. The latter array consists of |ASCII_code| characters, and
XX|font_name[nf]| is its first unoccupied position.  A horizontal motion
XXin the range |-4*font_space[f]<h<font_space[f]|
XXwill be treated as a `kern'.
XXA given character |c| is valid in font |f| if and only if
XX|char_width(f)(c)<>invalid_width|.
XXFinally, |char_width(f)(c)=width[width_base[f]+c]|, and |width_ptr| is the
XXfirst unused position of the |width| array.
XX  
XX@d char_width(#)==width[width_base[#]+char_end_width
XX@d invalid_width==@'17777777777
XX  
XX@<Glob...@>=
XX@!font_num:array [0..max_fonts] of integer; {external font numbers}
XX@!font_name:array [0..max_fonts] of 0..name_size; {starting positions
XX  of external font names}
XX@!names:array [0..name_size] of ASCII_code; {characters of names}
XX@!font_check_sum:array [0..max_fonts] of integer; {check sums}
XX@!font_scaled_size:array [0..max_fonts] of integer; {scale factors}
XX@!font_design_size:array [0..max_fonts] of integer; {design sizes}
XX@!font_mag:array [0..max_fonts] of integer;
XX@!font_space:array [0..max_fonts] of integer; {boundary between ``small''
XX  and ``large'' spaces}
XX@!font_used_on:array[0..max_fonts] of integer; {on which page last used}
XX@!width_base:array [0..max_fonts] of integer; {index into |width| table}
XX@=$Ema_Var On$@>
XX@!width:array [0..max_widths] of integer; {character widths, in \.{DVI} units}
XX@=$Ema_Var Off$@>
XX@!nf:0..max_fonts; {the number of known fonts}
XX@!width_ptr:0..max_widths; {the number of known character widths}
XX  
XX@ @<Set init...@>=
XXnf:=0; width_ptr:=0; font_name[0]:=0; font_space[0]:=0;
XXfont_used_on[0]:=0; 
XX  
XX@ It is, of course, a simple matter to print the name of a given font.
XX  
XX@p procedure print_font(@!f:integer); {|f| is an internal font number}
XXvar k:0..name_size; {index into |names|}
XXbegin if f=nf then print('UNDEFINED!')
XX@.UNDEFINED@>
XXelse  begin for k:=font_name[f] to font_name[f+1]-1 do
XX    print(xchr[names[k]]);
XX  end;
XXend;
XX  
XX@ The global variabls |pxl_check_sum|,
XX|pxl_design_size| are set from the \.{PXL} file.
XX  
XX@<Glob...@>=
XX@!pxl_check_sum:integer; {check sum found in |pxl_file|}
XX@!pxl_dptr:array [0..max_fonts] of integer; {directory pointers}
XX@!pxl_design_size:integer;
XX 
XX@ Here is a procedure that absorbs the necessary information from a
XX\.{PXL} file, assuming that the file has just been successfully opened.
XX(A complete description of
XX\.{PXL} file format appears elsewhere and will
XXnot be repeated here.) The procedure does not check the \.{PXL} file
XXfor validity, nor does it give explicit information about what is
XXwrong with a \.{PXL} file that proves to be invalid; \.{DVI}-reading
XXprograms need not do this, since \.{PXL} files are almost always valid.
XXThe procedure simply returns |false| if it
XXdetects anything amiss in the \.{PXL} data.
XX  
XXThere is a parameter, |z|, which represents the scaling factor being
XXused to compute the font dimensions; it must be in the range $0<z<2^{27}$.
XX  
XX@d read_pxl(#)==begin #:=pxl_file[nf]^.i; get(pxl_file[nf]); end
XX  
XX@p procedure in_PXL(@!z:integer); {input \.{PXL} data}
XXvar i,j,k,n:short; {indices etc.}
XX@!wp:0..max_widths; {new value of |width_ptr| after successful input}
XX@!raster_address:integer;
XX@!alpha,@!beta:integer; {quantities used in the scaling computation}
XXbegin
XX  if width_ptr+128>max_widths then begin
XX    print_nl;
XX    abort('Need larger width table');
XX  end;
XX  width_base[nf]:=width_ptr;
XX  wp:=width_ptr;
XX  @<Check the header ID@>;
XX  @<Read the trailer@>;
XX  @<Read the font directory@>;
XX  width_ptr:=wp;
XXend;
XX  
XX@ @<Check the header ID@>=
XXbegin
XX  seek_pxl(nf)(0);
XX  get(pxl_file[nf]);
XX  if pxl_file[nf]^.i <> pxl_id then
XX    bad_pxl('bad header id');
XXend
XX  
XX@ @<Read the trailer@>=
XXbegin
XX  seek(pxl_file[nf],s_pxl_file+1);
XX  get(pxl_file[nf]);
XX  if pxl_file[nf]^.i <> pxl_id then
XX    bad_pxl('bad trailer id');
XX  seek(pxl_file[nf],s_pxl_file-3);
XX  get(pxl_file[nf]);
XX  read_pxl(pxl_check_sum);
XX  read_pxl(font_mag[nf]);
XX  read_pxl(pxl_design_size);
XX  read_pxl(pxl_dptr[nf]);
XXend;
XX  
XX@ One important part of |in_PXL| is the width computation, which
XXinvolves multiplying the relative widths in the \.{PXL} file by the
XXscaling factor in the \.{DVI} file. This fixed-point multiplication
XXmust be done with precisely the same accuracy by all \.{DVI}-reading programs,
XXin order to validate the assumptions made by \.{DVI}-writing programs
XXlike \TeX82.
XX  
XXLet us therefore summarize what needs to be done. Each width in a \.{PXL}
XXfile appears as a four-byte quantity called a |fix_word|.  A |fix_word|
XXwhose respective bytes are $(a,b,c,d)$ represents the number
XX$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
XXb\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
XX-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
XX(No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
XXdimension must be less than 16.)  We want to multiply this quantity by the
XXinteger~|z|, which is known to be less than $2^{27}$. Let $\alpha=16z$.
XXIf $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
XX$d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
XX16, to obtain a multiplier less than $2^{23}$, and we can compensate for
XXthis later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
XX$\beta=2^{4-e}$; we shall compute
XX$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
XXor the same quantity minus $\alpha$ if $a=255$.  This calculation must be
XXdone exactly, for the reasons stated above; the following program does the
XXjob in a system-independent way, assuming that arithmetic is exact on
XXnumbers less than $2^{31}$ in magnitude.
XX  
XXThe following code computes pixel widths by simply rounding the \.{PXL}
XXwidths to the nearest integer number of pixels, based on the conversion factor
XX|conv| that converts \.{DVI} units to pixels. However, such a simple
XXformula will not be valid for all fonts, and it will often give results that
XXare off by $\pm1$ when a low-resolution font has been carefully
XXhand-fitted. For example, a font designer often wants to make the letter `m'
XXa pixel wider or narrower in order to make the font appear more consistent.
XX\.{DVI}-to-printer programs should therefore input the correct pixel width
XXinformation from font files whenever there is a chance that it may differ.
XXA warning message may also be desirable in the case that at least one character
XXis found whose pixel width differs from |conv*width| by more than a full pixel.
XX  
XXThose characters that are too large to be downloadable, are marked as such,
XXand will be transferred using raster graphics. We must be especially careful
XXin the y dimension, as the character must fit into the 255*255 box when
XXthe reference point is placed on the baseline. The baseline was set to
XX$255-$|baseline| pixel rows down from the top.
XX@^system dependencies@>
XX  
XX@d pixel_round(#)==round(conv*(#))
XX  
XX@<Read the font dir...@>=
XX@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
XXfor k:=0 to 127 do begin
XX  seek(pxl_file[nf],pxl_dptr[nf]+k*4+2);
XX  get(pxl_file[nf]);
XX  {is the charater too large ?}
XX  if (pxl_file[nf]^.i0 >= 128) or (pxl_file[nf]^.i1 >= 128) then
XX    status[wp]:=too_large
XX  else
XX    status[wp]:=not_loaded;
XX  get(pxl_file[nf]);
XX  get(pxl_file[nf]);
XX  if pxl_file[nf]^.i=0 then begin
XX    width[wp]:=invalid_width;
XX    pixel_width[wp]:=0  end
XX  else begin
XX    get(pxl_file[nf]);
XX    read_pxl_word(nf);
XX    width[wp]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
XX    if b0>0 then if b0<255 then bad_pxl('strange width for char ', k:1)
XX      else width[wp]:=width[wp]-alpha;
XX    pixel_width[wp]:=pixel_round(width[wp]);
XX  end;
XX  incr(wp);
XXend
XX  
XX@ @<Replace |z|...@>=
XXbegin alpha:=16*z; beta:=16;
XXwhile z>=@'40000000 do
XX  begin z:=z div 2; beta:=beta div 2;
XX  end;
XXend
XX 
XX@* Downloading information to the printer.
XXThe procedure |update_pos| is used to update the printer cursor position.
XXThis is the only place where the cursor is explicitely positioned.
XX  
XX@p procedure update_pos;
XXlabel done;
XXbegin if (lj_h<>hh)or(lj_v<>vv) then begin
XX  write_lj(@=#27'*p'@>);
XX  if (lj_h<>hh) then begin
XX    if abs(lj_h-hh) > hh div 10 then
XX      write_lj_f(hh:1)
XX    else if (lj_h<hh) then
XX      write_lj_f('+', hh-lj_h:1)
XX    else
XX      write_lj_f('-', lj_h-hh:1);
XX    if (lj_v<>vv) then
XX      write_lj_f('x')
XX    else begin
XX      write_lj_f('X');
XX      goto done;
XX    end
XX  end;
XX  if abs(lj_v-vv) > vv div 10 then
XX    write_lj_f(vv:1)
XX  else if (lj_v<vv) then
XX    write_lj_f('+', vv-lj_v:1)
XX  else
XX    write_lj_f('-', lj_v-vv:1);
XX  write_lj_f('Y');
XXend;
XXdone:
XXlj_h:=hh; lj_v:=vv;
XXend;
XX 
XX@ This procedure downloads a single character from a \.{PXL} file.
XX@^system dependencies@>
XX  
XX@p procedure download_char(p:integer);
XXvar i,j:short;
XX@!pixel_wd,@!pixel_ht:integer;
XX@!offsets,char_wd,char_ht,delta_x:i2c;
XX@!raster_address:integer;
XX  
XXbegin
XX  seek(pxl_file[cur_font],pxl_dptr[cur_font]+p*4+2);
XX  get(pxl_file[cur_font]);
XX  pixel_wd:=pxl_file[cur_font]^.i0;
XX  pixel_ht:=pxl_file[cur_font]^.i1;
XX  get(pxl_file[cur_font]);
XX  offsets:=pxl_file[cur_font]^;
XX  get(pxl_file[cur_font]);
XX  raster_address:=pxl_file[cur_font]^.i;
XX  @<Download character header@>;
XX  seek(pxl_file[cur_font],raster_address+2);
XX  for j:=1 to pixel_ht do begin
XX    for i:=1 to (pixel_wd-1) div 8 + 1 do begin
XX      if (i-1) mod 4 = 0 then
XX        get(pxl_file[cur_font]);
XX      write_lj(pxl_file[cur_font]^.c[(i-1) mod 4]);
XX    end;
XX  end
XXend;
XX  
XX@ Download a character header.
XX  
XX@d data_bytes==(pixel_ht*(((pixel_wd-1) div 8)+1))
XX  
XX@<Download character header@>=
XXbegin
XX  if pixel_ht - offsets.i1 > baseline then begin
XX    char_status(cur_font)(p):=pixel_ht-offsets.i1;
XX    offsets.i1:=pixel_ht;  end
XX  else
XX    char_status(cur_font)(p):=loaded_ok;
XX  offsets.i0:=-offsets.i0;
XX  char_wd.i0:=pixel_wd;
XX  char_ht.i0:=pixel_ht;
XX  delta_x.i0:=char_pixel_width(cur_font)(p)*4;
XX  write_lj(@=#27'*c'@>,font_num[cur_font]:1,'d',
XX    ord(vis_chr(p)):1,@='E'#27'(s'@>,
XX    data_bytes+16:1,'W'+
XX    @=#4#0#14#1#0#0@>,
XX    offsets.c[0], offsets.c[1],
XX    offsets.c[2], offsets.c[3],
XX    char_wd.c[0], char_wd.c[1],
XX    char_ht.c[0], char_ht.c[1],
XX    delta_x.c[0], delta_x.c[1]);
XXend
XX  
XX@ This procedure transfers a character in raster form.
XXThis is used for characters which are too large to be stored in
XXthe font memory of the printer.
XX@^system dependencies@>
XX  
XX@p procedure raster_char(p:integer);
XXvar
XXi,j,n:short;
XX@!pixel_ht,@!pixel_wd,@!x_offset,@!y_offset:short;
XX@!raster_address:integer;
XX  
XXbegin
XX  seek(pxl_file[cur_font],pxl_dptr[cur_font]+p*4+2);
XX  get(pxl_file[cur_font]);
XX  pixel_wd:=pxl_file[cur_font]^.i0;
XX  pixel_ht:=pxl_file[cur_font]^.i1;
XX  get(pxl_file[cur_font]);
XX  x_offset:=pxl_file[cur_font]^.i0;
XX  y_offset:=pxl_file[cur_font]^.i1;
XX  get(pxl_file[cur_font]);
XX  raster_address:=pxl_file[cur_font]^.i;
XX  hh:=hh-x_offset; vv:=vv-y_offset;
XX  update_pos;
XX  write_lj(@=#27'*r1A'@>);
XX  seek(pxl_file[cur_font],raster_address+2);
XX  for j:=1 to pixel_ht do begin
XX    write_lj(#27'*b',(pixel_wd-1) div 8 + 1:1, 'W');
XX    for i:=1 to (pixel_wd-1) div 8 + 1 do begin
XX      if (i-1) mod 4 = 0 then
XX        get(pxl_file[cur_font]);
XX      write_lj(pxl_file[cur_font]^.c[(i-1) mod 4]);
XX    end;
XX  end;
XX  lj_v:=lj_v+pixel_ht;
XX  hh:=hh+char_pixel_width(cur_font)(p); vv:=vv+y_offset;
XX  write_lj(@=#27'*rB'@>);
XXend;
XX  
XX@* Optional modes of output.
XXThe starting page is specified by giving a sequence of 1 to 10 numbers or
XXasterisks separated by dots. For example, the specification `\.{1.*.-5}'
XXcan be used to refer to a page output by \TeX\ when $\.{\\count0}=1$
XXand $\.{\\count2}=-5$. (Recall that |bop| commands in a \.{DVI} file
XXare followed by ten `count' values.) An asterisk matches any number,
XXso the `\.*' in `\.{1.*.-5}' means that \.{\\count1} is ignored when
XXspecifying the first page. If several pages match the given specification,
XX\.{DVIplus} will begin with the earliest such page in the file. The
XXdefault specification `\.*' (which matches all pages) therefore denotes
XXthe page at the beginning of the file.
XX  
XXAnother option is the page offset. That is the point on the physical
XXprinter page where the point (0,0) in the DVI file is mapped.
XX  
XXNormally \.{DVIplus} uses default values for the options.
XXIt can be started in such a way that it engages
XXthe user in a brief dialog so that the
XXoptions will be specified.
XX@^system dependencies@>
XX  
XX@<Glob...@>=
XX@!max_pages:integer; {at most this many |bop..eop| pages will be printed}
XX@!resolution:real; {pixels per inch}
XX@!new_mag:integer; {if positive, overrides the postamble's magnification}
XX@!copies:integer;
XX@!h_offset,@!v_offset:integer; {offset where to put (0,0) on physical page}
XX  
XX@ The starting page specification is recorded in two global arrays called
XX|start_count| and |start_there|. For example, `\.{1.*.-5}' is represented
XXby |start_there[0]=true|, |start_count[0]=1|, |start_there[1]=false|,
XX|start_there[2]=true|, |start_count[2]=-5|.
XXWe also set |start_vals=2|, to indicate that count 2 was the last one
XXmentioned. The other values of |start_count| and |start_there| are not
XXimportant, in this example.
XX  
XX@<Glob...@>=
XX@!start_count:array[0..9] of integer; {count values to select starting page}
XX@!start_there:array[0..9] of boolean; {is the |start_count| value relevant?}
XX@!start_vals:0..9; {the last count considered significant}
XX@!count:array[0..9] of integer; {the count values on the current page}
XX  
XX@ @<Set init...@>=
XXmax_pages:=100; start_vals:=0; start_there[0]:=false; copies:=1;
XXresolution:=300.0; new_mag:=0;
XXh_offset:=210; v_offset:=100;
XX  
XX@ Here is a simple subroutine that tests if the current page might be the
XXstarting page.
XX 
XX@p function start_match:boolean; {does |count| match the starting spec?}
XXvar k:0..9;  {loop index}
XX@!match:boolean; {does everything match so far?}
XXbegin match:=true;
XXfor k:=0 to start_vals do
XX  if start_there[k]and(start_count[k]<>count[k]) then match:=false;
XXstart_match:=match;
XXend;
XX  
XX@ The |input_ln| routine waits for the user to type a line at his or her
XXterminal; then it puts ASCII-code equivalents for the characters on that line
XXinto the |buffer| array. The |term_in| file is used for terminal input,
XXand |term_out| for terminal output.
XX@^system dependencies@>
XX  
XX@<Glob...@>=
XX@!buffer:array[0..terminal_line_length] of ASCII_code;
XX@!term_in:text_file; {the terminal, considered as an input file}
XX@!term_out:text_file; {the terminal, considered as an output file}
XX@!arg_index:short; {which command line argument is being processed}
XX@!interactive:boolean;
XX  
XX@ Since the terminal is being used for both input and output, some systems
XXneed a special routine to make sure that the user can see a prompt message
XXbefore waiting for input based on that message. (Otherwise the message
XXmay just be sitting in a hidden buffer somewhere, and the user will have
XXno idea what the program is waiting for.) We shall call a system-dependent
XXsubroutine |update_terminal| in order to avoid this problem.
XX@^system dependencies@>
XX  
XX@d update_terminal == prompt(term_out) {empty the terminal output buffer}
XX  
XX@ During the dialog, \.{DVIplus} will treat the first blank space in a
XXline as the end of that line. Therefore |input_ln| makes sure that there
XXis always at least one blank space in |buffer|.
XX@^system dependencies@>
XX  
XX@p procedure input_ln; {inputs a line from the terminal}
XXvar k:0..terminal_line_length;
XXbegin update_terminal;
XXk:=0;
XXif eoln(term_in) then read_ln(term_in)
XXelse begin while (k<terminal_line_length)and not eoln(term_in) do
XX  begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
XX  end;
XX  read_ln(term_in);
XXend;
XXbuffer[k]:=" ";
XXend;
XX  
XX@ The global variable |buf_ptr| is used while scanning each line of input;
XXit points to the first unread character in |buffer|.
XX  
XX@<Glob...@>=
XX@!buf_ptr:0..terminal_line_length; {the number of characters read}
XX  
XX@ Here is a routine that scans a (possibly signed) integer and computes
XXthe decimal value. If no decimal integer starts at |buf_ptr|, the
XXvalue 0 is returned. The integer should be less than $2^{31}$ in
XXabsolute value.

XX@p function get_integer:integer;
XXvar x:integer; {accumulates the value}
XX@!negative:boolean; {should the value be negated?}
XXbegin if buffer[buf_ptr]="-" then
XX  begin negative:=true; incr(buf_ptr);
XX  end
XXelse negative:=false;
XXx:=0;
XXwhile (buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9") do
XX  begin x:=10*x+buffer[buf_ptr]-"0"; incr(buf_ptr);
XX  end;
XXif negative then get_integer:=-x @+ else get_integer:=x;
XXend;
XX  
XX@ The selected options are put into global variables by the |dialog|
XXprocedure, which is called just as \.{DVIplus} begins.
XX@^system dependencies@>
XX  
XX@p procedure dialog;
XXlabel 2,3,6,7,8,9,99;
XXvar i,j,k:short;
XXbegin rewrite(term_out, '1', 'NOCCTL'); {prepare the terminal for output}
XXreset(term_in, '1'); {and for input}
XXprint_ln(banner);
XX@<Get flags, check if interactive@>;
XX@<Determine the desired |start_count| values@>;
XX@<Determine the desired |max_pages|@>;
XX@<Determine the number of copies@>;
XX@<Determine the page offset@>;
XX@<Print all the selected options@>;
XXend;
XX 
XX@ @<Get flags, check if interactive@>=
XXarg_index:=1; interactive:=false;
XXrepeat
XX  i:=parameters(arg_index,cur_name,name_length);
XX  if cur_name[1]='-' then begin
XX    incr(arg_index);
XX    if i=1 then
XX      interactive:=true
XX    else case cur_name[2] of
XX      'i','I':interactive:=true;
XX    othercases begin end;
XX    endcases;
XX  end;
XXuntil cur_name[1]<>'-';
XXif not interactive then goto 99;
XX  
XX@ @<Determine the desired |start...@>=
XX2: print('Starting page (default=*): ');
XXinput_ln; buf_ptr:=0; k:=0;
XXif buffer[0]<>" " then
XX  repeat if buffer[buf_ptr]="*" then
XX    begin start_there[k]:=false; incr(buf_ptr);
XX    end
XX  else  begin start_there[k]:=true; start_count[k]:=get_integer;
XX    end;
XX  if (k<9)and(buffer[buf_ptr]=".") then
XX    begin incr(k); incr(buf_ptr);
XX    end
XX  else if buffer[buf_ptr]=" " then start_vals:=k
XX  else  begin print('Type, e.g., 1.*.-5 to specify the ');
XX    print_ln('first page with \count0=1, \count2=-5.');
XX    goto 2;
XX    end;
XX  until start_vals=k
XX  
XX@ @<Determine the desired |max_pages|@>=
XX3: print('Maximum number of pages (default=100): ');
XXinput_ln; buf_ptr:=0;
XXif buffer[0]<>" " then
XX  begin max_pages:=get_integer;
XX  if max_pages<=0 then
XX    begin print_ln('Please type a positive number.');
XX    goto 3;
XX    end;
XX  end
XX  
XX@ @<Determine the number of copies@>=
XX6: print('Number of copies (default=1): ');
XXinput_ln; buf_ptr:=0;
XXif buffer[0]<>" " then
XX  if (buffer[0]>="0")and(buffer[0]<="9") then copies:=get_integer
XX  else  begin print('Type a positive integer to specify ');
XX    print_ln('the number of copies.');
XX    goto 6;
XX    end
XX 
XX@ @<Determine the page offset@>=
XX7: print('Page offset in dots (default=210,100): ');
XXinput_ln; buf_ptr:=0;
XXif buffer[0]=" " then goto 9;
XXif ((buffer[0]>="0")and(buffer[0]<="9")) or (buffer[0]="-") then
XX h_offset:=get_integer
XXelse goto 8;
XXif (buffer[buf_ptr]=",") then incr(buf_ptr);
XXif (buffer[buf_ptr]=" ") then incr(buf_ptr);
XXif ((buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9")) or
XX   (buffer[buf_ptr]="-") then begin
XX  v_offset:=get_integer;
XX  goto 9; end
XXelse goto 8;
XX8:print('Specify a dot coordinate pair where the (0,0) ');
XX  print_ln('point will be placed.');
XX  goto 7;
XX9:
XX  
XX@ After the dialog is over, we print the options so that the user
XXcan see what \.{DVIplus} thought was specified.
XX  
XX@<Print all the selected options@>=
XX99:print_ln('Options selected:');
XX@.Options selected@>
XXprint('  Starting page = ');
XXfor k:=0 to start_vals do
XX  begin if start_there[k] then print(start_count[k]:1)
XX  else print('*');
XX  if k<start_vals then print('.')
XX  else print_nl;
XX  end;
XXprint_ln('  Maximum number of pages = ',max_pages:1);
XXprint_ln('  Page offset = (', h_offset:1, ',', v_offset:1, ')');
XX  
XX@* Defining fonts.
XX\.{DVIplus} reads the postamble first and loads
XXall of the fonts defined there; then it processes the pages.
XX  
XX@ Approximate the desired magnification to an available one.
XXThe ``magig numbers'' that the desired magnification
XXis compared to are calculated as
XX$1500*1.2^m$, where $m = 1/4,3/4,1.5,2.5,3.5,4.5$
XX  
XX@p function approx_mag(f:integer;d_mag:integer):integer;
XX  
XXbegin
XX  if d_mag < 1569 then
XX    approx_mag := 1500
XX  else if d_mag < 1720 then
XX    approx_mag := 1643
XX  else if d_mag < 1971 then
XX    approx_mag := 1800
XX  else if d_mag < 2366 then
XX    approx_mag := 2160
XX  else if d_mag < 2839 then
XX    approx_mag := 2592
XX  else if d_mag < 3407 then
XX    approx_mag := 3110
XX  else if d_mag < 4089 then
XX    approx_mag := 3732
XX  else
XX    approx_mag:= 4479;
XXend;
XX  
XX@ The following subroutine does the necessary things when a \\{fnt\_def}
XXcommand is being processed.
XX  
XX@p procedure define_font(@!e:integer); {|e| is an external font number}
XXvar f:0..max_fonts;
XX@!p:integer; {length of the area/directory spec}
XX@!n:integer; {length of the font name proper}
XX@!c,@!q,@!d:integer; {check sum, scaled size, and design size}
XX@!m:integer; {|mag| corrected for 300 pixels/inch}
XX@!r:0..name_length; {index into |cur_name|}
XX@!j,@!k:0..name_size; {indices into |names|}
XX@!mismatch:boolean; {do names disagree?}
XXbegin if nf=max_fonts then abort('DVIplus capacity exceeded (max fonts=',
XX    max_fonts:1,')!');
XX@.DVIplus capacity exceeded...@>
XXfont_num[nf]:=e; f:=0;
XXwhile font_num[f]<>e do incr(f);
XX@<Read the font parameters into position for font |nf|, and
XX  print the font name@>;
XX  if f<nf then print_ln('---this font was already defined!');
XX@.this font was already defined@>
XX  @<Load the new font, unless there are problems@>
XXend;
XX  
XX@ @<Read the font parameters into position for font |nf|...@>=
XXc:=signed_quad; font_check_sum[nf]:=c;@/
XXq:=signed_quad; font_scaled_size[nf]:=q;@/
XXd:=signed_quad; font_design_size[nf]:=d;@/
XXp:=get_byte; n:=get_byte;
XXif font_name[nf]+n+p>name_size then
XX  abort('DVIplus capacity exceeded (name size=',name_size:1,')');
XX@.DVIplus capacity exceeded...@>
XXfont_name[nf+1]:=font_name[nf]+n+p;
XXif n+p=0 then abort('Null font name')
XX@.Null font name@>
XXelse for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=get_byte;
XXfont_used_on[nf+1]:=0;
XXincr(nf);
XXif f=nf-1 then begin
XX  print('Font ', e:1, ': '); print_font(nf-1);
XX  update_terminal;
XXend;
XXdecr(nf)
XX 
XX@ @<Load the new font, unless there are problems@>=
XXbegin
XX@<Compute |desired_mag|@>;
XX@<Move font name into the |cur_name| string@>;
XXif not open_pxl_file(nf) then begin
XX  print_nl;
XX  abort('Cannot open PXL file ', cur_name); end
XX@.Cannot open PXL file@>
XXelse  begin if (q<=0)or(q>=@'1000000000) then begin
XX    print_nl;
XX    abort('PXL file not loaded, bad scale (',q:1,')!'); end
XX@.bad scale@>
XX  else if (d<=0)or(d>=@'1000000000) then begin
XX    print_nl;
XX    abort('PXL file not loaded, bad design size (',d:1,')!'); end
XX@.bad design size@>
XX  else begin
XX    in_PXL(q);
XX    @<Finish loading the new font info@>;
XX  end
XXend
XXend
XX  
XX@ @<Compute |desired_mag|@>=
XXdesired_mag:=round((mag * q * (300.0/200.0))/d+0.5);
XX  
XX@ @<Finish loading...@>=
XXbegin font_space[nf]:=q div 6; {this is a 3-unit ``thin space''}
XXif (c<>0)and(pxl_check_sum<>0)and(c<>pxl_check_sum) then
XX  begin print(' ---beware: check sums do not agree!');
XX@.beware: check sums do not agree@>
XX@.check sums do not agree@>
XX  print(' (',c:1,' vs. ',pxl_check_sum:1,')');
XX  end;
XXd:=round((100.0*conv*q)/(true_conv*d));
XXif d<>100 then
XX  print(' (magnified ',d:1,'%)');
XX@.this font is magnified@>
XXincr(nf); {now the new font is officially present}
XXfont_space[nf]:=0; {for |out_space| and |out_vmove|}
XXend
XX  
XX@ If |p=0|, i.e., if no font directory has been specified, \.{DVIplus}
XXuses the default font directory, which is a
XXsystem-dependent place where the standard fonts are kept.
XX  
XXIn RTE--A, the \.{PXL} files are kept in directories called
XX\.{/TeX/Fonts/MagXXXX}, where \.{XXXX} is the magnification.
XXThe string variable |default_prefix| contains the prefix of these names.
XX@^system dependencies@>
XX  
XX@d default_prefix_name=='/TeX/Fonts/Mag'
XX@d default_prefix_length=14 {change this to the correct length}
XX  
XX@<Glob...@>=
XX@!default_prefix:packed array[1..default_prefix_length] of char;
XX  
XX@ @<Set init...@>=
XXdefault_prefix:=default_prefix_name;
XX  
XX@ The string |cur_name| is set to the external name of the
XX\.{PXL} file for the current font and magnification.
XX@^system dependencies@>
XX  
XX@<Move font name into the |cur_name| string@>=
XXfor k:=1 to name_length do cur_name[k]:=' ';
XXif p=0 then
XX  begin for k:=1 to default_prefix_length do
XX    cur_name[k]:=default_prefix[k];
XX  r:=default_prefix_length;
XX  incr(r);
XX  best_mag:=approx_mag(font_name[nf],desired_mag);
XX  m:=best_mag;
XX  cur_name[r]:=xchr[ m div 1000 + xord['0']];
XX  incr(r);
XX  cur_name[r]:=xchr[ (m div 100) mod 10 + xord['0']];
XX  incr(r);
XX  cur_name[r]:=xchr[ (m div 10) mod 10 + xord['0']];
XX  incr(r);
XX  cur_name[r]:=xchr[ m mod 10 + xord['0']];
XX  incr(r);
XX  cur_name[r]:='/'; end
XXelse
XX  r:=0;
XX  
XXfor k:=font_name[nf] to font_name[nf+1]-1 do
XX  begin incr(r);
XX  if r+4>name_length then
XX    abort('DVIplus capacity exceeded (max font name length=',
XX      name_length:1,')!');
XX@.DVIplus capacity exceeded...@>
XX  cur_name[r]:=xchr[names[k]];
XX  end;
XXcur_name[r+1]:='.'; cur_name[r+2]:='P'; cur_name[r+3]:='X'; cur_name[r+4]:='L'
XX 
@//E*O*F LJ.2.3//
chmod u=rw,g=rw,o=rw LJ.2.3
 
exit 0