home *** CD-ROM | disk | FTP | other *** search
- Subject: v06i015: TeX DVI driver for LaserJet+ (texdvi2lj), Part3/3
- Newsgroups: mod.sources
- Approved: rs@mirror.UUCP
-
- Submitted by: Tor Lillqvist <talcott!seismo!mcvax!santra!tml>
- Mod.sources: Volume 6, Issue 15
- Archive-name: texdvi2lj/Part3
-
- [ 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. --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.3.3
-
- echo x - LJ.3.3
- sed 's/^XX//' > "LJ.3.3" <<'@//E*O*F LJ.3.3//'
- XX@* Interpreting the {\tentex DVI} file.
- XXThe main work of \.{DVIplus} is accomplished by the |do_page| procedure,
- XXwhich produces the output for an entire page, assuming that the |bop|
- XXcommand for that page has already been processed. This procedure is
- XXessentially an interpretive routine that reads and acts on the \.{DVI}
- XXcommands.
- XX
- XX@ The definition of \.{DVI} files refers to six registers,
- XX$(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units. In practice,
- XXwe also need registers |hh| and |vv|, the pixel analogs of $h$ and $v$,
- XXsince it is not always true that |hh=pixel_round(h)| or
- XX|vv=pixel_round(v)|.
- XXThe |lj_h| and |lj_v| registers hold the current actual cursor position
- XXof the printer.
- XX
- XXThe stack of $(h,v,w,x,y,z)$ values is represented by eight arrays
- XXcalled |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|.
- XX
- XX@<Glob...@>=
- XX@!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values}
- XX@!lj_h,@!lj_v:integer; {current cursor position}
- XX@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack:
- XX array [0..stack_size] of integer; {pushed down values in \.{DVI} units}
- XX@!hhstack,@!vvstack:
- XX array [0..stack_size] of integer; {pushed down values in pixels}
- XX
- XX@ Three characteristics of the pages (their |max_v|, |max_h|, and
- XX|max_s|) are specified in the postamble, and a warning message
- XXis printed if these limits are exceeded. Actually |max_v| is set to
- XXthe maximum height plus depth of a page, and |max_h| to the maximum width,
- XXfor purposes of page layout. Since characters can legally be set outside
- XXof the page boundaries, it is not an error when |max_v| or |max_h| is
- XXexceeded. But |max_s| should not be exceeded.
-
- XXThe postamble also specifies the total number of pages; \.{DVIplus}
- XXchecks to see if this total is accurate.
-
- XX@<Glob...@>=
- XX@!max_v:integer; {the value of |abs(v)| should probably not exceed this}
- XX@!max_h:integer; {the value of |abs(h)| should probably not exceed this}
- XX@!max_s:integer; {the stack depth should not exceed this}
- XX@!max_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels}
- XX@!total_pages:integer; {the stated total number of pages}
- XX@!page_count:integer; {the total number of pages seen so far}
-
- XX@ @<Set init...@>=
- XXmax_v:=@'17777777777-99; max_h:=@'17777777777-99; max_s:=stack_size+1;@/
- XXmax_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0;
-
- XX@ Before we get into the details of |do_page|, it is convenient to
- XXconsider a simpler routine that computes the first parameter of each
- XXopcode.
-
- XX@d four_cases(#)==#,#+1,#+2,#+3
- XX@d eight_cases(#)==four_cases(#),four_cases(#+4)
- XX@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
- XX@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
- XX@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)
-
- XX@p function first_par(o:eight_bits):integer;
- XXbegin case o of
- XXsixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
- XX first_par:=o-set_char_0;
- XXset1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte;
- XXset1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes;
- XXset1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes;
- XXright1,w1,x1,down1,y1,z1: first_par:=signed_byte;
- XXright1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair;
- XXright1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio;
- XXset1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3,
- XX fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad;
- XXnop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0;
- XXw0: first_par:=w;
- XXx0: first_par:=x;
- XXy0: first_par:=y;
- XXz0: first_par:=z;
- XXsixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0;
- XXend;
- XXend;
-
- XX@ Here is another subroutine that we need: It computes the number of
- XXpixels in the height or width of a rule. Characters and rules will line up
- XXproperly if the sizes are computed precisely as specified here. (Since
- XX|conv| is computed with some floating-point roundoff error, in a
- XXmachine-dependent way, format designers who are tailoring something for a
- XXparticular resolution should not plan their measurements to come out to an
- XXexact integer number of pixels; they should compute things so that the
- XXrule dimensions are a little less than an integer number of pixels, e.g.,
- XX4.99 instead of 5.00.)
-
- XX@p function rule_pixels(x:integer):integer;
- XX {computes $\lceil|conv|\cdot x\rceil$}
- XXvar n:integer;
- XXbegin n:=trunc(conv*x);
- XXif n<conv*x then rule_pixels:=n+1 @+ else rule_pixels:=n;
- XXend;
-
- XX@ Strictly speaking, the |do_page| procedure is really a function with
- XXside effects, not a `\&{procedure}'; it returns the value |false| if
- XX\.{DVIplus} should be aborted because of some unusual happening. The
- XXsubroutine is organized as a typical interpreter, with a multiway branch
- XXon the command code followed by |goto| statements leading to routines that
- XXfinish up the activities common to different commands. We will use the
- XXfollowing labels:
- XX
- XX@d fin_set=41 {label for commands that set or put a character}
- XX@d fin_rule=42 {label for commands that set or put a rule}
- XX@d move_right=43 {label for commands that change |h|}
- XX@d move_down=44 {label for commands that change |v|}
- XX@d change_font=46 {label for commands that change |cur_font|}
- XX
- XX@ Some \PASCAL\ compilers severely restrict the length of procedure bodies,
- XXso we shall split |do_page| into two parts, one of which is
- XXcalled |special_cases|. The different parts communicate with each other
- XXvia the global variables mentioned above, together with the following ones:
- XX
- XX@<Glob...@>=
- XX@!s:integer; {current stack size}
- XX@!ss:integer; {stack size to print}
- XX@!cur_font:integer; {current internal font number}
- XX@!prev_font:integer;
- XX@!fonts_in_use:integer; {how many are currently loaded}
- XX@!fonts_on_page:integer; {how many fonts used on current page}
- XX
- XX@ @<Set init...@>=
- XXfonts_in_use:=0;
- XX
- XX@ Here is the overall setup.
- XX
- XX@p @t\4@>@<Declare the function called |special_cases|@>@;
- XXprocedure do_page;
- XXlabel fin_set,fin_rule,move_right,done,exit;
- XXvar o:eight_bits; {operation code of the current command}
- XX@!p,@!q:integer; {parameters of the current command}
- XXpp,qq:integer;
- XXi,j,k:integer;
- XX@!a:integer; {byte number of the current command}
- XX@!hhh:integer; {|h|, rounded to the nearest pixel}
- XX@!height,pitch:i2c;
- XXbegin cur_font:=nf; {set current font undefined}
- XXprev_font:=-1;
- XXs:=0; h:=round(h_offset/conv); v:=round(v_offset/conv);
- XXw:=0; x:=0; y:=0; z:=0; hh:=pixel_round(h); vv:=pixel_round(v);
- XXlj_h:=-10000; lj_v:=-10000;
- XX {initialize the state variables}
- XXwhile true do @<Translate the next command in the \.{DVI} file@>;
- XXexit:
- XXend;
- XX
- XX@
- XX
- XX@d error(#)==print_ln('% ',#)
- XX
- XX@<Translate the next command...@>=
- XXbegin a:=cur_loc;
- XXo:=get_byte; p:=first_par(o);
- XXif eof_dvi_file then bad_dvi('file ended prematurely');
- XX@.the file ended prematurely@>
- XX@<Start translation of command |o| and |goto| the appropriate label to
- XX finish the job@>;
- XXfin_set: @<Finish a command that either sets or puts a character, then
- XX |goto move_right| or |done|@>;
- XXfin_rule: @<Finish a command that either sets or puts a rule, then
- XX |goto move_right| or |done|@>;
- XXmove_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>;
- XXdone:
- XXend
- XX
- XX@ The multiway switch in |first_par|, above, was organized by the length
- XXof each command; the one in |do_page| is organized by the semantics.
- XX
- XX@<Start translation...@>=
- XXif o<set_char_0+128 then @<Translate a |set_char| command@>
- XXelse case o of
- XX four_cases(set1),
- XX four_cases(put1): bad_dvi('illegal character (', p:1, ')');
- XX set_rule,
- XX put_rule: goto fin_rule;
- XX @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
- XX @t\4@>@<Cases for horizontal motion@>@;
- XX othercases if special_cases(o,p,a) then goto done
- XX else bad_dvi(' ')
- XX endcases
- XX
- XX@ @<Declare the function called |special_cases|@>=
- XXfunction special_cases(@!o:eight_bits;@!p,@!a:integer):boolean;
- XXlabel change_font,move_down,done,9998;
- XXvar q:integer; {parameter of the current command}
- XXq1,q2:integer; {dummies for |fnt_def|}
- XX@!k:integer; {loop index}
- XX@!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
- XX@!pure:boolean; {is the command error-free?}
- XX@!vvv:integer; {|v|, rounded to the nearest pixel}
- XXbegin pure:=true;
- XXcase o of
- XX@t\4@>@<Cases for vertical motion@>@;
- XX@t\4@>@<Cases for fonts@>@;
- XXfour_cases(xxx1): @<Translate an |xxx| command and |goto done|@>;
- XXpre: begin error('preamble command within a page!'); goto 9998;
- XX end;
- XX@.preamble command within a page@>
- XXpost,post_post: begin error('postamble command within a page!'); goto 9998;
- XX@.postamble command within a page@>
- XX end;
- XXothercases begin error('undefined command ',o:1,'!');
- XX goto done;
- XX@.undefined command@>
- XX end
- XXendcases;
- XXmove_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>;
- XXchange_font: @<Finish a command that changes the current font,
- XX then |goto done|@>;
- XX9998: pure:=false;
- XXdone: special_cases:=pure;
- XXend;
- XX
- XX@ @<Cases for commands |nop|, |bop|, \dots, |pop|@>=
- XXnop: goto done;
- XXbop: begin error('bop occurred before eop'); bad_dvi(' ');
- XX@.bop occurred before eop@>
- XX end;
- XXeop: begin
- XX write_lj(#12);
- XX if s<>0 then error('stack not empty at end of page (level ',
- XX s:1,')!');
- XX@.stack not empty...@>
- XX return;
- XX end;
- XXpush: begin
- XX if s=max_s_so_far then
- XX begin max_s_so_far:=s+1;
- XX if s=max_s then error('deeper than claimed in postamble!');
- XX@.deeper than claimed...@>
- XX@.push deeper than claimed...@>
- XX if s=stack_size then
- XX abort('DVIplus capacity exceeded (stack size=',
- XX stack_size:1,')');
- XX end;
- XX hstack[s]:=h; vstack[s]:=v; wstack[s]:=w;
- XX xstack[s]:=x; ystack[s]:=y; zstack[s]:=z;
- XX hhstack[s]:=hh; vvstack[s]:=vv; incr(s); ss:=s-1; goto done;
- XX end;
- XXpop: begin
- XX if s=0 then error('(illegal at level zero)!')
- XX else begin decr(s); hh:=hhstack[s]; vv:=vvstack[s];
- XX h:=hstack[s]; v:=vstack[s]; w:=wstack[s];
- XX x:=xstack[s]; y:=ystack[s]; z:=zstack[s];
- XX end;
- XX ss:=s; goto done;
- XX end;
- XX
- XX@ Rounding to the nearest pixel is best done in the manner shown here, so as
- XXto be inoffensive to the eye: When the horizontal motion is small, like a
- XXkern, |hh| changes by rounding the kern; but when the motion is large, |hh|
- XXchanges by rounding the true position |h| so that accumulated rounding errors
- XXdisappear. We allow a larger space in the negative direction than in
- XXthe positive one, because \TeX\ makes comparatively
- XXlarge backspaces when it positions accents.
- XX
- XX@d out_space==begin
- XX if (p>=font_space[cur_font])or(p<=-4*font_space[cur_font]) then
- XX hh:=pixel_round(h+p)
- XX else hh:=hh+pixel_round(p);
- XX q:=p; goto move_right;
- XXend
- XX
- XX@<Cases for horizontal motion@>=
- XXfour_cases(right1):out_space;
- XXw0,four_cases(w1):begin w:=p; out_space;
- XX end;
- XXx0,four_cases(x1):begin x:=p; out_space;
- XX end;
- XX
- XX@ Vertical motion is done similarly, but with the threshold between
- XX``small'' and ``large'' increased by a factor of five. The idea is to make
- XXfractions like ``$1\over2$'' round consistently, but to absorb accumulated
- XXrounding errors in the baseline-skip moves.
- XX
- XX@d out_vmove==begin
- XX if abs(p)>=5*font_space[cur_font] then vv:=pixel_round(v+p)
- XX else vv:=vv+pixel_round(p);
- XX goto move_down;
- XXend
- XX
- XX@<Cases for vertical motion@>=
- XXfour_cases(down1):out_vmove;
- XXy0,four_cases(y1):begin y:=p; out_vmove;
- XX end;
- XXz0,four_cases(z1):begin z:=p; out_vmove;
- XX end;
- XX
- XX@ @<Cases for fonts@>=
- XXsixty_four_cases(fnt_num_0),
- XXfour_cases(fnt1):
- XX goto change_font;
- XXfour_cases(fnt_def1): begin
- XX @<Skip a |fnt_def| command@>;
- XX goto done;
- XX end;
- XX
- XX@ @<Skip a |fnt_def| command@>=
- XXq:=signed_quad; q:=signed_quad; q:=signed_quad;
- XXq1:=get_byte; q2:=get_byte;
- XXfor k:=1 to q1+q2 do q:=get_byte;
- XX
- XX@ @<Translate an |xxx| command and |goto done|@>=
- XXbegin bad_char:=false;
- XXfor k:=1 to p do
- XX begin q:=get_byte;
- XX if not ((q>=" ")and(q<="~")) then
- XX bad_char:=true;
- XX end;
- XXif bad_char then error('non-ASCII character in xxx command!');
- XX@.non-ASCII character...@>
- XXgoto done;
- XXend
- XX
- XX@ @<Translate a |set_char|...@>=
- XXgoto fin_set
- XX
- XX@ This is the code that checks whether the next character to be
- XXprinted in |cur_font| has occurred previously.
- XXIf not, the character data is downloaded to the printer. If the character
- XXis too large, the data is sent as a raster image.
- XXIf |cur_font| is also new, the font header is first downloaded.
- XXThe number of fonts stored in the printer is kept at |max_printer_fonts|
- XXmaximum. When more fonts are needed, the least recently used one is
- XXdeleted.
- XX
- XXThe value of |max_printer_fonts| was choosed by waving a magig rod;
- XXit would be more appropriate to calculate exactly how many bytes of
- XXthe user-available memory in the LaserJet+ is in use, and base the
- XXdecisions whether we must delete some fonts on that.
- XX(Why can't you ask the printer how much memory it has left?).
- XX
- XXThere should also be a test whether the number of fonts on a page
- XXexceeds the maximum 16.
- XX
- XX@<Finish a command that either sets or puts a character...@>=
- XXif p<0 then p:=255-((-1-p) mod 256)
- XXelse if p>=256 then p:=p mod 256; {width computation for oriental fonts}
- XX@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
- XXif (p>127) then q:=invalid_width
- XXelse q:=char_width(cur_font)(p);
- XXif q=invalid_width then
- XX begin error('character ',p:1,' invalid in font ');
- XX@.character $c$ invalid...@>
- XX print_font(cur_font);
- XX if cur_font<>nf then print('!'); {font |nf| has `\.!' in its name}
- XX end;
- XXif font_used_on[cur_font]=0 then begin
- XX if fonts_in_use=max_printer_fonts then
- XX @<Delete least recently used font@>;
- XX incr(fonts_on_page);
- XX @<Download font header@> end
- XXelse if font_used_on[cur_font]<page_count then
- XX incr(fonts_on_page);
- XXif fonts_on_page>max_fonts_on_page then
- XX error('too many fonts on this page');
- XXfont_used_on[cur_font]:=page_count;
- XXif cur_font <> prev_font then
- XX write_lj(@=#27'('@>,font_num[cur_font]:1,'X');
- XXprev_font:=cur_font;
- XXif char_status(cur_font)(p) = too_large then
- XX raster_char(p)
- XXelse begin
- XX if char_status(cur_font)(p) = not_loaded then
- XX download_char(p);
- XX if char_status(cur_font)(p) > 0 then begin
- XX vv:=vv+char_status(cur_font)(p);
- XX update_pos;
- XX write_lj(vis_chr(p));
- XX vv:=vv-char_status(cur_font)(p); end
- XX else begin
- XX update_pos;
- XX write_lj(vis_chr(p));
- XX end;
- XX lj_h:=lj_h+char_pixel_width(cur_font)(p);
- XXend;
- XXif o>=put1 then begin
- XX hh:=hh-char_pixel_width(cur_font)(p);
- XX goto done;
- XXend;
- XXif q=invalid_width then q:=0
- XXelse hh:=hh+char_pixel_width(cur_font)(p);
- XXgoto move_right
- XX
- XX@ @<Download font header@>=
- XXbegin
- XX height.i0:=4*round(font_design_size[cur_font]*
- XX font_mag[cur_font]/1000.0*conv);
- XX if (height.i0 < 0) or (height.i0>10922) then
- XX height.i0:= 10922;
- XX pitch.i0:=height.i0-20; {is this value used for anything in the printer??}
- XX
- XX write_lj(@=#27'*c'@>,font_num[cur_font]:1,
- XX {char cell 255*255 pixels max}
- XX {baseline distance set to $255-$|baseline| }
- XX @='D'#27')s26W'#0#26#0#1#0#0#0@>, chr(255-baseline),
- XX @=#0#255#0#255#0#1#1#21@>,
- XX pitch.c[0], pitch.c[1], height.c[0], height.c[1],
- XX @=#0#0#0#0#0#0#27'*c4F'@>);
- XX incr(fonts_in_use);
- XXend
- XX
- XX@ @<Delete least recently...@>=
- XXbegin
- XX j:=9999;
- XX k:=nf;
- XX for i:=0 to nf-1 do
- XX if font_used_on[i] < j then begin
- XX j:=font_used_on[i];
- XX k:=i;
- XX end;
- XX write_lj(@=#27'*c'@>,font_num[k]:1,'d2F');
- XX font_used_on[k]:=0;
- XX for i:=0 to 127 do
- XX if (char_status(k)(i) > 0) or (char_status(k)(i) = loaded_ok) then
- XX char_status(k)(i):=not_loaded;
- XX decr(fonts_in_use);
- XXend
- XX
- XX@ @<Finish a command that either sets or puts a rule...@>=
- XXq:=signed_quad;
- XXqq:=rule_pixels(q);
- XXpp:=rule_pixels(p);
- XXif (p>0) and (q>0) then begin
- XX vv:=vv-pp;
- XX update_pos;
- XX write_lj(@=#27'*c'@>,qq:1,'a',pp:1,'b0P');
- XX vv:=vv+pp;
- XXend;
- XXif o=put_rule then goto done;
- XXhh:=hh+qq;
- XXgoto move_right
- XX
- XX@ A sequence of consecutive rules, or consecutive characters in a fixed-width
- XXfont whose width is not an integer number of pixels, can cause |hh| to drift
- XXfar away from a correctly rounded value. \.{DVIplus} ensures that the
- XXamount of drift will never exceed |max_drift| pixels.
- XX
- XXSince \.{DVIplus} is intended to diagnose strange errors, it checks
- XXcarefully to make sure that |h| and |v| do not get out of range.
- XXNormal \.{DVI}-reading programs need not do this.
- XX
- XX@d infinity==@'17777777777 {$\infty$ (approximately)}
- XX@d max_drift=2 {we insist that abs|(hh-pixel_round(h))<=max_drift|}
- XX
- XX@<Finish a command that sets |h:=h+q|, then |goto done|@>=
- XXif (h>0)and(q>0) then if h>infinity-q then
- XX begin error('arithmetic overflow! parameter changed from ',
- XX@.arithmetic overflow...@>
- XX q:1,' to ',infinity-h:1);
- XX q:=infinity-h;
- XX end;
- XXif (h<0)and(q<0) then if -h>q+infinity then
- XX begin error('arithmetic overflow! parameter changed from ',
- XX q:1, ' to ',(-h)-infinity:1);
- XX q:=(-h)-infinity;
- XX end;
- XXhhh:=pixel_round(h+q);
- XXif abs(hhh-hh)>max_drift then begin
- XX if hhh>hh then hh:=hhh-max_drift
- XX else hh:=hhh+max_drift;
- XXend;
- XXh:=h+q;
- XXif abs(h)>max_h_so_far then
- XX begin if abs(h)-round(h_offset/conv)>max_h+99 then
- XX begin error('warning: h > ',max_h:1,'!');
- XX@.warning: |h|...@>
- XX max_h:=abs(h);
- XX end;
- XX max_h_so_far:=abs(h);
- XX end;
- XXgoto done
- XX
- XX@ @<Finish a command that sets |v:=v+p|, then |goto done|@>=
- XXif (v>0)and(p>0) then if v>infinity-p then
- XX begin error('arithmetic overflow! parameter changed from ',
- XX@.arithmetic overflow...@>
- XX p:1,' to ',infinity-v:1);
- XX p:=infinity-v;
- XX end;
- XXif (v<0)and(p<0) then if -v>p+infinity then
- XX begin error('arithmetic overflow! parameter changed from ',
- XX p:1, ' to ',(-v)-infinity:1);
- XX p:=(-v)-infinity;
- XX end;
- XXvvv:=pixel_round(v+p);
- XXif abs(vvv-vv)>max_drift then begin
- XX if vvv>vv then vv:=vvv-max_drift
- XX else vv:=vvv+max_drift;
- XXend;
- XXv:=v+p;
- XXif abs(v)>max_v_so_far then
- XX begin if abs(v)-round(v_offset/conv)>max_v+99 then
- XX begin error('warning: v > ',max_v:1,'!');
- XX@.warning: |v|...@>
- XX max_v:=abs(v);
- XX end;
- XX max_v_so_far:=abs(v);
- XX end;
- XXgoto done
- XX
- XX@ @<Finish a command that changes the current font...@>=
- XXbegin
- XX font_num[nf]:=p; cur_font:=0;
- XX while font_num[cur_font]<>p do incr(cur_font);
- XX goto done
- XXend
- XX
- XX@* Using the backpointers.
- XXFirst comes a routine that illustrates how to find the postamble quickly.
- XX
- XX@<Find the postamble, working back from the end@>=
- XXn:=dvi_length;
- XXif n<53 then bad_dvi('only ',n:1,' bytes long');
- XX@.only n bytes long@>
- XXm:=n-4;
- XXrepeat if m=0 then bad_dvi('all 223s');
- XX@.all 223s@>
- XXmove_to_byte(m); k:=get_byte; decr(m);
- XXuntil k<>223;
- XXif k<>dvi_id then bad_dvi('ID byte is ',k:1);
- XX@.ID byte is wrong@>
- XXmove_to_byte(m-3); q:=signed_quad;
- XXif (q<0)or(q>m-33) then bad_dvi('post pointer ',q:1,' at byte ',m-3:1);
- XX@.post pointer is wrong@>
- XXmove_to_byte(q); k:=get_byte;
- XXif k<>post then bad_dvi('byte ',q:1,' is not post');
- XX@.byte n is not post@>
- XXpost_loc:=q; first_backpointer:=signed_quad
- XX
- XX@ Note that the last steps of the above code save the locations of the
- XXthe |post| byte and the final |bop|. We had better declare these global
- XXvariables, together with another one that we will need shortly.
- XX
- XX@<Glob...@>=
- XX@!post_loc:integer; {byte location where the postamble begins}
- XX@!first_backpointer:integer; {the pointer following |post|}
- XX@!start_loc:integer; {byte location of the first page to process}
- XX@!start_inx:integer; {index into |page_start| for first page}
- XX@!last_loc:integer; {byte localtion of last page to process}
- XX@!page_start:array[1..max_bops] of integer; {pointers to |bop|s}
- XX
- XX@ The next routines follow the backpointers
- XXto move through a \.{DVI} file in reverse order. \.{DVIplus} does this because
- XXit wants to print the pages backwards as the LaserJet stacks
- XXthem with the printed side up.
- XX
- XXFirst we search for the starting page and the last page.
- XX
- XX@<Scan for page range to print@>=
- XXq:=post_loc; p:=first_backpointer; start_loc:=-1;
- XXrepeat
- XX {now |q| points to a |post| or |bop| command; |p>=0| is prev pointer}
- XX if p>q-46 then
- XX bad_dvi('page link ',p:1,' after byte ',q:1);
- XX@.page link wrong...@>
- XX q:=p; move_to_byte(q);
- XX k:=get_byte;
- XX if k=bop then incr(page_count)
- XX else bad_dvi('byte ',q:1,' is not bop');
- XX@.byte n is not bop@>
- XX if page_count>max_bops then bad_dvi('there are too many pages');
- XX@.there are too many pages@>
- XX page_start[page_count]:=q;
- XX for k:=0 to 9 do count[k]:=signed_quad;
- XX if start_match then begin start_loc:=q; start_inx:=page_count; end;
- XX p:=signed_quad;
- XXuntil p<0;
- XXif start_loc<0 then abort('starting page number could not be found!');
- XX@.starting page number...@>
- XXif (start_inx > max_pages) then
- XX last_loc:=page_start[start_inx-max_pages+1]
- XXelse
- XX last_loc:=page_start[1];
- XXif page_count<>total_pages then
- XX print_ln('there are really ',page_count:1,
- XX ' pages, not ',total_pages:1,'!');
- XX@.there are really n pages@>
- XX
- XX@ This is the code that really goes through the pages to be printed
- XX(in reverse order). It starts from the page pointed to by |last_loc| and
- XXproceeds up to |start_loc|.
- XX
- XXThe code shown here uses a convention that has proved to be useful:
- XXIf the starting page was specified as, e.g., `\.{1.*.-5}', then
- XXall page numbers in the file are displayed by showing the values of
- XXcounts 0, 1, and~2, separated by dots. Such numbers can, for example,
- XXbe displayed on the console of a printer when it is working on that
- XXpage.
- XX
- XX@<Translate the page range in reverse order@>=
- XXpage_count:=0;
- XXq:=last_loc;
- XXrepeat
- XX move_to_byte(q); k:=get_byte;
- XX incr(page_count);
- XX fonts_on_page:=0;
- XX for k:=0 to 9 do count[k]:=signed_quad;
- XX q:=signed_quad;
- XX print('[');
- XX for k:=0 to start_vals do
- XX begin print(count[k]:1);
- XX if k<start_vals then print('.');
- XX end;
- XX update_terminal;
- XX do_page;
- XX print('] ');
- XX update_terminal;
- XXuntil q<start_loc;
- XX
- XX@* Reading the postamble.
- XXNow imagine that we are reading the \.{DVI} file and positioned just
- XXfour bytes after the |post| command. That, in fact, is the situation,
- XXwhen the following part of \.{DVIplus} is called upon to read, translate,
- XXand check the rest of the postamble.
- XX
- XX@p procedure read_postamble;
- XXvar k:integer; {loop index}
- XX@!p,@!q,@!m:integer; {general purpose registers}
- XXbegin post_loc:=cur_loc-5;
- XX@.Postamble starts at byte n@>
- XXif signed_quad<>numerator then
- XX print_ln('numerator doesn''t match the preamble!');
- XX@.numerator doesn't match@>
- XXif signed_quad<>denominator then
- XX print_ln('denominator doesn''t match the preamble!');
- XX@.denominator doesn't match@>
- XXif signed_quad<>mag then if new_mag=0 then
- XX print_ln('magnification doesn''t match the preamble!');
- XX@.magnification doesn't match@>
- XXmax_v:=signed_quad; max_h:=signed_quad;@/
- XXmax_s:=get_two_bytes; total_pages:=get_two_bytes;@/
- XXif total_pages>max_bops then
- XX bad_dvi('enormous number of pages (', total_pages:1, ')');
- XX@.enormous number of pages@>
- XX@<Process the font definitions of the postamble@>;
- XX@<Make sure that the end of the file is well-formed@>;
- XXend;
- XX
- XX@ No warning is given when |max_h_so_far| exceeds |max_h| by less than~100,
- XXsince 100 units is invisibly small; it's approximately the wavelength of
- XXvisible light, in the case of \TeX\ output. Rounding errors can be expected
- XXto make |h| and |v| slightly more than |max_h| and |max_v|, every once in
- XXa~while; hence small discrepancies are not cause for alarm.
- XX
- XX@ When we get to the present code, the |post_post| command has
- XXjust been read.
-
- XX@<Make sure that the end of the file is well-formed@>=
- XXq:=signed_quad;
- XXif q<>post_loc then
- XX print_ln('bad postamble pointer in byte ',cur_loc-4:1,'!');
- XX@.bad postamble pointer@>
- XXm:=get_byte;
- XXif m<>dvi_id then print_ln('identification in byte ',cur_loc-1:1,
- XX@.identification...should be n@>
- XX ' should be ',dvi_id:1,'!');
- XXk:=cur_loc; m:=223;
- XXwhile (m=223)and not eof_dvi_file do m:=get_byte;
- XXif not eof_dvi_file then bad_dvi('signature in byte ',cur_loc-1:1,
- XX@.signature...should be...@>
- XX ' should be 223')
- XXelse if cur_loc<k+4 then
- XX print_ln('not enough signature bytes at end of file (',
- XX@.not enough signature bytes...@>
- XX cur_loc-k:1,')');
-
- XX@ @<Process the font definitions...@>=
- XXrepeat k:=get_byte;
- XXif (k>=fnt_def1)and(k<fnt_def1+4) then
- XX begin p:=first_par(k); define_font(p); print_nl; k:=nop;
- XX end;
- XXuntil k<>nop;
- XXif k<>post_post then
- XX print_ln('byte ',cur_loc-1:1,' is not postpost!')
- XX@.byte n is not postpost@>
- XX
- XX@* The main program.
- XXNow we are ready to put it all together. This is where \.{DVIplus} starts,
- XXand where it ends.
- XX
- XX@p begin dont_catch_errors; initialize; {get all variables initialized}
- XXdialog; {set up all the options}
- XX@<Process the preamble@>;
- XX@<Find the postamble, working back from the end@>;
- XXread_postamble;
- XXprint_ln('Total of ', nf:1, ' fonts.');
- XX@<Scan for page range to print@>;
- XXwrite_lj(@=#27'E'#27'&l'@>, copies:1, @='X'#27'*t300R'@>);
- XX@<Translate the page range...@>;
- XXwrite_lj(@=#27'&l1X'@>);
- XXwrite_ln(laser_file,'_');
- XXprint_nl
- XXend.
- XX
- XX@ The main program needs a few global variables in order to do its work.
- XX
- XX@<Glob...@>=
- XX@!k,@!m,@!n,@!p,@!q:integer; {general purpose registers}
- XX
- XX@ A \.{DVI}-reading program that reads the postamble first need not look at the
- XXpreamble; but \.{DVIplus} looks at the preamble in order to do error
- XXchecking, and to display the introductory comment.
- XX
- XX@<Process the preamble@>=
- XXif not open_dvi_file then
- XX bad_dvi('cannot open file');
- XX@<Open and lock |laser_file|@>;
- XXp:=get_byte; {fetch the first byte}
- XXif p<>pre then bad_dvi('first byte isn''t start of preamble!');
- XX@.First byte isn't...@>
- XXp:=get_byte; {fetch the identification byte}
- XXif p<>dvi_id then
- XX print_ln('identification in byte 1 should be ',dvi_id:1,'!');
- XX@.identification...should be n@>
- XX@<Compute the conversion factor@>;
- XXp:=get_byte; {fetch the length of the introductory comment}
- XXprint('''');
- XXwhile p>0 do
- XX begin decr(p); print(xchr[get_byte]);
- XX end;
- XXprint_ln('''')
-
- XX@ The conversion factor |conv| is figured as follows: There are exactly
- XX|n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch,
- XXand |resolution| pixels per inch. Then we have to adjust this
- XXby the stated amount of magnification.
- XX
- XX@<Compute the conversion factor@>=
- XXnumerator:=signed_quad; denominator:=signed_quad;
- XXif numerator<=0 then bad_dvi('numerator is ',numerator:1);
- XX@.numerator is wrong@>
- XXif denominator<=0 then bad_dvi('denominator is ',denominator:1);
- XX@.denominator is wrong@>
- XXconv:=(numerator/254000.0)*(resolution/denominator);
- XXmag:=signed_quad;
- XXif new_mag>0 then mag:=new_mag
- XXelse if mag<=0 then bad_dvi('magnification is ',mag:1);
- XX@.magnification is wrong@>
- XXtrue_conv:=conv; conv:=true_conv*(mag/1000.0);
- XX
- XX@* System-dependent changes.
- XXThis section should be replaced, if necessary, by changes to the program
- XXthat are necessary to make \.{DVIplus} work at a particular installation.
- XXIt is usually best to design your change file so that all changes to
- XXprevious sections preserve the section numbering; then everybody's version
- XXwill be consistent with the printed program. More extensive changes,
- XXwhich introduce new sections, can be inserted here; then only the index
- XXitself will get a new section number.
- XX@^system dependencies@>
-
- XX@* Index.
- XXPointers to error messages appear here together with the section numbers
- XXwhere each ident\-i\-fier is used.
- XX-------------------------------------- end of last part ----------------
-
-
- @//E*O*F LJ.3.3//
- chmod u=rw,g=rw,o=rw LJ.3.3
-
- exit 0
-