home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume6 / texdvi2lj / part3 < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  28.8 KB

  1. Subject: v06i015:  TeX DVI driver for LaserJet+ (texdvi2lj), Part3/3
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.UUCP
  4.  
  5. Submitted by: Tor Lillqvist <talcott!seismo!mcvax!santra!tml>
  6. Mod.sources: Volume 6, Issue 15
  7. Archive-name: texdvi2lj/Part3
  8.  
  9. [ I have split this submission into three pieces named LJ.1.3,
  10.   LJ.2.3, and LJ.2.3.  Unpack each piece and do
  11.       "cat LJ.[123].3 >dvi2lj.web"
  12.   This is not great, but is the best way I could think of to
  13.   distribute a large single-file source; if you have a better
  14.   way, please let me know.  --r$]
  15.  
  16. Here is a DVI (TeX output) driver for the HP LaserJet+.  This version
  17. is for the Pascal/1000 compiler on HP1000 machines running RTE-A (and
  18. my TeX implementation), but it should be fairly easy to convert to
  19. other TeX implementations, compilers and operating systems.
  20.  
  21. DVIplus is based on the DVItype program. It downloads only those
  22. characters actually used. Pages are printed in reverse.
  23.  
  24. # This is a shell archive.  Remove anything before this line,
  25. # then unpack it by saving it in a file and typing "sh file".
  26. # Contents:  LJ.3.3
  27.  
  28. echo x - LJ.3.3
  29. sed 's/^XX//' > "LJ.3.3" <<'@//E*O*F LJ.3.3//'
  30. XX@* Interpreting the {\tentex DVI} file.
  31. XXThe main work of \.{DVIplus} is accomplished by the |do_page| procedure,
  32. XXwhich produces the output for an entire page, assuming that the |bop|
  33. XXcommand for that page has already been processed. This procedure is
  34. XXessentially an interpretive routine that reads and acts on the \.{DVI}
  35. XXcommands.
  36. XX  
  37. XX@ The definition of \.{DVI} files refers to six registers,
  38. XX$(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units.  In practice,
  39. XXwe also need registers |hh| and |vv|, the pixel analogs of $h$ and $v$,
  40. XXsince it is not always true that |hh=pixel_round(h)| or
  41. XX|vv=pixel_round(v)|.
  42. XXThe |lj_h| and |lj_v| registers hold the current actual cursor position
  43. XXof the printer.
  44. XX  
  45. XXThe stack of $(h,v,w,x,y,z)$ values is represented by eight arrays
  46. XXcalled |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|.
  47. XX  
  48. XX@<Glob...@>=
  49. XX@!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values}
  50. XX@!lj_h,@!lj_v:integer; {current cursor position}
  51. XX@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack:
  52. XX  array [0..stack_size] of integer; {pushed down values in \.{DVI} units}
  53. XX@!hhstack,@!vvstack:
  54. XX  array [0..stack_size] of integer; {pushed down values in pixels}
  55. XX  
  56. XX@ Three characteristics of the pages (their |max_v|, |max_h|, and
  57. XX|max_s|) are specified in the postamble, and a warning message
  58. XXis printed if these limits are exceeded. Actually |max_v| is set to
  59. XXthe maximum height plus depth of a page, and |max_h| to the maximum width,
  60. XXfor purposes of page layout. Since characters can legally be set outside
  61. XXof the page boundaries, it is not an error when |max_v| or |max_h| is
  62. XXexceeded. But |max_s| should not be exceeded.
  63.  
  64. XXThe postamble also specifies the total number of pages; \.{DVIplus}
  65. XXchecks to see if this total is accurate.
  66.  
  67. XX@<Glob...@>=
  68. XX@!max_v:integer; {the value of |abs(v)| should probably not exceed this}
  69. XX@!max_h:integer; {the value of |abs(h)| should probably not exceed this}
  70. XX@!max_s:integer; {the stack depth should not exceed this}
  71. XX@!max_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels}
  72. XX@!total_pages:integer; {the stated total number of pages}
  73. XX@!page_count:integer; {the total number of pages seen so far}
  74.  
  75. XX@ @<Set init...@>=
  76. XXmax_v:=@'17777777777-99; max_h:=@'17777777777-99; max_s:=stack_size+1;@/
  77. XXmax_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0;
  78.  
  79. XX@ Before we get into the details of |do_page|, it is convenient to
  80. XXconsider a simpler routine that computes the first parameter of each
  81. XXopcode.
  82.  
  83. XX@d four_cases(#)==#,#+1,#+2,#+3
  84. XX@d eight_cases(#)==four_cases(#),four_cases(#+4)
  85. XX@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
  86. XX@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
  87. XX@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)
  88.  
  89. XX@p function first_par(o:eight_bits):integer;
  90. XXbegin case o of
  91. XXsixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
  92. XX  first_par:=o-set_char_0;
  93. XXset1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte;
  94. XXset1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes;
  95. XXset1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes;
  96. XXright1,w1,x1,down1,y1,z1: first_par:=signed_byte;
  97. XXright1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair;
  98. XXright1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio;
  99. XXset1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3,
  100. XX  fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad;
  101. XXnop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0;
  102. XXw0: first_par:=w;
  103. XXx0: first_par:=x;
  104. XXy0: first_par:=y;
  105. XXz0: first_par:=z;
  106. XXsixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0;
  107. XXend;
  108. XXend;
  109.  
  110. XX@ Here is another subroutine that we need: It computes the number of
  111. XXpixels in the height or width of a rule. Characters and rules will line up
  112. XXproperly if the sizes are computed precisely as specified here.  (Since
  113. XX|conv| is computed with some floating-point roundoff error, in a
  114. XXmachine-dependent way, format designers who are tailoring something for a
  115. XXparticular resolution should not plan their measurements to come out to an
  116. XXexact integer number of pixels; they should compute things so that the
  117. XXrule dimensions are a little less than an integer number of pixels, e.g.,
  118. XX4.99 instead of 5.00.)
  119.  
  120. XX@p function rule_pixels(x:integer):integer;
  121. XX  {computes $\lceil|conv|\cdot x\rceil$}
  122. XXvar n:integer;
  123. XXbegin n:=trunc(conv*x);
  124. XXif n<conv*x then rule_pixels:=n+1 @+ else rule_pixels:=n;
  125. XXend;
  126.  
  127. XX@ Strictly speaking, the |do_page| procedure is really a function with
  128. XXside effects, not a `\&{procedure}'; it returns the value |false| if
  129. XX\.{DVIplus} should be aborted because of some unusual happening. The
  130. XXsubroutine is organized as a typical interpreter, with a multiway branch
  131. XXon the command code followed by |goto| statements leading to routines that
  132. XXfinish up the activities common to different commands. We will use the
  133. XXfollowing labels:
  134. XX 
  135. XX@d fin_set=41 {label for commands that set or put a character}
  136. XX@d fin_rule=42 {label for commands that set or put a rule}
  137. XX@d move_right=43 {label for commands that change |h|}
  138. XX@d move_down=44 {label for commands that change |v|}
  139. XX@d change_font=46 {label for commands that change |cur_font|}
  140. XX  
  141. XX@ Some \PASCAL\ compilers severely restrict the length of procedure bodies,
  142. XXso we shall split |do_page| into two parts, one of which is
  143. XXcalled |special_cases|. The different parts communicate with each other
  144. XXvia the global variables mentioned above, together with the following ones:
  145. XX  
  146. XX@<Glob...@>=
  147. XX@!s:integer; {current stack size}
  148. XX@!ss:integer; {stack size to print}
  149. XX@!cur_font:integer; {current internal font number}
  150. XX@!prev_font:integer;
  151. XX@!fonts_in_use:integer; {how many are currently loaded}
  152. XX@!fonts_on_page:integer; {how many fonts used on current page}
  153. XX  
  154. XX@ @<Set init...@>=
  155. XXfonts_in_use:=0;
  156. XX  
  157. XX@ Here is the overall setup.
  158. XX  
  159. XX@p @t\4@>@<Declare the function called |special_cases|@>@;
  160. XXprocedure do_page;
  161. XXlabel fin_set,fin_rule,move_right,done,exit;
  162. XXvar o:eight_bits; {operation code of the current command}
  163. XX@!p,@!q:integer; {parameters of the current command}
  164. XXpp,qq:integer;
  165. XXi,j,k:integer;
  166. XX@!a:integer; {byte number of the current command}
  167. XX@!hhh:integer; {|h|, rounded to the nearest pixel}
  168. XX@!height,pitch:i2c;
  169. XXbegin cur_font:=nf; {set current font undefined}
  170. XXprev_font:=-1;
  171. XXs:=0; h:=round(h_offset/conv); v:=round(v_offset/conv);
  172. XXw:=0; x:=0; y:=0; z:=0; hh:=pixel_round(h); vv:=pixel_round(v);
  173. XXlj_h:=-10000; lj_v:=-10000;
  174. XX  {initialize the state variables}
  175. XXwhile true do @<Translate the next command in the \.{DVI} file@>;
  176. XXexit:
  177. XXend;
  178. XX  
  179. XX@
  180. XX  
  181. XX@d error(#)==print_ln('% ',#)
  182. XX  
  183. XX@<Translate the next command...@>=
  184. XXbegin a:=cur_loc;
  185. XXo:=get_byte; p:=first_par(o);
  186. XXif eof_dvi_file then bad_dvi('file ended prematurely');
  187. XX@.the file ended prematurely@>
  188. XX@<Start translation of command |o| and |goto| the appropriate label to
  189. XX  finish the job@>;
  190. XXfin_set: @<Finish a command that either sets or puts a character, then
  191. XX    |goto move_right| or |done|@>;
  192. XXfin_rule: @<Finish a command that either sets or puts a rule, then
  193. XX    |goto move_right| or |done|@>;
  194. XXmove_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>;
  195. XXdone:
  196. XXend
  197. XX  
  198. XX@ The multiway switch in |first_par|, above, was organized by the length
  199. XXof each command; the one in |do_page| is organized by the semantics.
  200. XX  
  201. XX@<Start translation...@>=
  202. XXif o<set_char_0+128 then @<Translate a |set_char| command@>
  203. XXelse case o of
  204. XX  four_cases(set1),
  205. XX  four_cases(put1): bad_dvi('illegal character (', p:1, ')');
  206. XX  set_rule,
  207. XX  put_rule: goto fin_rule;
  208. XX  @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
  209. XX  @t\4@>@<Cases for horizontal motion@>@;
  210. XX  othercases if special_cases(o,p,a) then goto done
  211. XX             else bad_dvi(' ')
  212. XX  endcases
  213. XX  
  214. XX@ @<Declare the function called |special_cases|@>=
  215. XXfunction special_cases(@!o:eight_bits;@!p,@!a:integer):boolean;
  216. XXlabel change_font,move_down,done,9998;
  217. XXvar q:integer; {parameter of the current command}
  218. XXq1,q2:integer; {dummies for |fnt_def|}
  219. XX@!k:integer; {loop index}
  220. XX@!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
  221. XX@!pure:boolean; {is the command error-free?}
  222. XX@!vvv:integer; {|v|, rounded to the nearest pixel}
  223. XXbegin pure:=true;
  224. XXcase o of
  225. XX@t\4@>@<Cases for vertical motion@>@;
  226. XX@t\4@>@<Cases for fonts@>@;
  227. XXfour_cases(xxx1): @<Translate an |xxx| command and |goto done|@>;
  228. XXpre: begin error('preamble command within a page!'); goto 9998;
  229. XX  end;
  230. XX@.preamble command within a page@>
  231. XXpost,post_post: begin error('postamble command within a page!'); goto 9998;
  232. XX@.postamble command within a page@>
  233. XX  end;
  234. XXothercases begin error('undefined command ',o:1,'!');
  235. XX  goto done;
  236. XX@.undefined command@>
  237. XX  end
  238. XXendcases;
  239. XXmove_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>;
  240. XXchange_font: @<Finish a command that changes the current font,
  241. XX  then |goto done|@>;
  242. XX9998: pure:=false;
  243. XXdone: special_cases:=pure;
  244. XXend;
  245. XX  
  246. XX@ @<Cases for commands |nop|, |bop|, \dots, |pop|@>=
  247. XXnop: goto done;
  248. XXbop: begin error('bop occurred before eop'); bad_dvi(' ');
  249. XX@.bop occurred before eop@>
  250. XX  end;
  251. XXeop: begin
  252. XX  write_lj(#12);
  253. XX  if s<>0 then error('stack not empty at end of page (level ',
  254. XX    s:1,')!');
  255. XX@.stack not empty...@>
  256. XX  return;
  257. XX  end;
  258. XXpush: begin
  259. XX  if s=max_s_so_far then
  260. XX    begin max_s_so_far:=s+1;
  261. XX    if s=max_s then error('deeper than claimed in postamble!');
  262. XX@.deeper than claimed...@>
  263. XX@.push deeper than claimed...@>
  264. XX    if s=stack_size then
  265. XX      abort('DVIplus capacity exceeded (stack size=',
  266. XX        stack_size:1,')');
  267. XX    end;
  268. XX  hstack[s]:=h; vstack[s]:=v; wstack[s]:=w;
  269. XX  xstack[s]:=x; ystack[s]:=y; zstack[s]:=z;
  270. XX  hhstack[s]:=hh; vvstack[s]:=vv; incr(s); ss:=s-1; goto done;
  271. XX  end;
  272. XXpop: begin
  273. XX  if s=0 then error('(illegal at level zero)!')
  274. XX  else  begin decr(s); hh:=hhstack[s]; vv:=vvstack[s];
  275. XX    h:=hstack[s]; v:=vstack[s]; w:=wstack[s];
  276. XX    x:=xstack[s]; y:=ystack[s]; z:=zstack[s];
  277. XX    end;
  278. XX  ss:=s; goto done;
  279. XX  end;
  280. XX  
  281. XX@ Rounding to the nearest pixel is best done in the manner shown here, so as
  282. XXto be inoffensive to the eye: When the horizontal motion is small, like a
  283. XXkern, |hh| changes by rounding the kern; but when the motion is large, |hh|
  284. XXchanges by rounding the true position |h| so that accumulated rounding errors
  285. XXdisappear. We allow a larger space in the negative direction than in
  286. XXthe positive one, because \TeX\ makes comparatively
  287. XXlarge backspaces when it positions accents.
  288. XX  
  289. XX@d out_space==begin
  290. XX  if (p>=font_space[cur_font])or(p<=-4*font_space[cur_font]) then
  291. XX    hh:=pixel_round(h+p)
  292. XX  else hh:=hh+pixel_round(p);
  293. XX  q:=p; goto move_right;
  294. XXend
  295. XX  
  296. XX@<Cases for horizontal motion@>=
  297. XXfour_cases(right1):out_space;
  298. XXw0,four_cases(w1):begin w:=p; out_space;
  299. XX  end;
  300. XXx0,four_cases(x1):begin x:=p; out_space;
  301. XX  end;
  302. XX  
  303. XX@ Vertical motion is done similarly, but with the threshold between
  304. XX``small'' and ``large'' increased by a factor of five. The idea is to make
  305. XXfractions like ``$1\over2$'' round consistently, but to absorb accumulated
  306. XXrounding errors in the baseline-skip moves.
  307. XX  
  308. XX@d out_vmove==begin
  309. XX  if abs(p)>=5*font_space[cur_font] then vv:=pixel_round(v+p)
  310. XX  else vv:=vv+pixel_round(p);
  311. XX  goto move_down;
  312. XXend
  313. XX  
  314. XX@<Cases for vertical motion@>=
  315. XXfour_cases(down1):out_vmove;
  316. XXy0,four_cases(y1):begin y:=p; out_vmove;
  317. XX  end;
  318. XXz0,four_cases(z1):begin z:=p; out_vmove;
  319. XX  end;
  320. XX  
  321. XX@ @<Cases for fonts@>=
  322. XXsixty_four_cases(fnt_num_0),
  323. XXfour_cases(fnt1):
  324. XX  goto change_font;
  325. XXfour_cases(fnt_def1): begin
  326. XX  @<Skip a |fnt_def| command@>;
  327. XX  goto done;
  328. XX  end;
  329. XX  
  330. XX@ @<Skip a |fnt_def| command@>=
  331. XXq:=signed_quad; q:=signed_quad; q:=signed_quad;
  332. XXq1:=get_byte; q2:=get_byte;
  333. XXfor k:=1 to q1+q2 do q:=get_byte;
  334. XX  
  335. XX@ @<Translate an |xxx| command and |goto done|@>=
  336. XXbegin bad_char:=false;
  337. XXfor k:=1 to p do
  338. XX  begin q:=get_byte;
  339. XX  if not ((q>=" ")and(q<="~")) then
  340. XX    bad_char:=true;
  341. XX  end;
  342. XXif bad_char then error('non-ASCII character in xxx command!');
  343. XX@.non-ASCII character...@>
  344. XXgoto done;
  345. XXend
  346. XX  
  347. XX@ @<Translate a |set_char|...@>=
  348. XXgoto fin_set
  349. XX  
  350. XX@ This is the code that checks whether the next character to be
  351. XXprinted in |cur_font| has occurred previously.
  352. XXIf not, the character data is downloaded to the printer. If the character
  353. XXis too large, the data is sent as a raster image.
  354. XXIf |cur_font| is also new, the font header is first downloaded.
  355. XXThe number of fonts stored in the printer is kept at |max_printer_fonts|
  356. XXmaximum. When more fonts are needed, the least recently used one is
  357. XXdeleted.
  358. XX  
  359. XXThe value of |max_printer_fonts| was choosed by waving a magig rod;
  360. XXit would be more appropriate to calculate exactly how many bytes of
  361. XXthe user-available memory in the LaserJet+ is in use, and base the
  362. XXdecisions whether we must delete some fonts on that.
  363. XX(Why can't you ask the printer how much memory it has left?).
  364. XX  
  365. XXThere should also be a test whether the number of fonts on a page
  366. XXexceeds the maximum 16.
  367. XX  
  368. XX@<Finish a command that either sets or puts a character...@>=
  369. XXif p<0 then p:=255-((-1-p) mod 256)
  370. XXelse if p>=256 then p:=p mod 256; {width computation for oriental fonts}
  371. XX@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
  372. XXif (p>127) then q:=invalid_width
  373. XXelse q:=char_width(cur_font)(p);
  374. XXif q=invalid_width then
  375. XX  begin error('character ',p:1,' invalid in font ');
  376. XX@.character $c$ invalid...@>
  377. XX  print_font(cur_font);
  378. XX  if cur_font<>nf then print('!'); {font |nf| has `\.!' in its name}
  379. XX  end;
  380. XXif font_used_on[cur_font]=0 then begin
  381. XX  if fonts_in_use=max_printer_fonts then
  382. XX    @<Delete least recently used font@>;
  383. XX  incr(fonts_on_page);
  384. XX  @<Download font header@> end
  385. XXelse if font_used_on[cur_font]<page_count then
  386. XX  incr(fonts_on_page);
  387. XXif fonts_on_page>max_fonts_on_page then
  388. XX  error('too many fonts on this page');
  389. XXfont_used_on[cur_font]:=page_count;
  390. XXif cur_font <> prev_font then
  391. XX  write_lj(@=#27'('@>,font_num[cur_font]:1,'X');
  392. XXprev_font:=cur_font;
  393. XXif char_status(cur_font)(p) = too_large then
  394. XX  raster_char(p)
  395. XXelse begin
  396. XX  if char_status(cur_font)(p) = not_loaded then
  397. XX    download_char(p);
  398. XX  if char_status(cur_font)(p) > 0 then begin
  399. XX    vv:=vv+char_status(cur_font)(p);
  400. XX    update_pos;
  401. XX    write_lj(vis_chr(p));
  402. XX    vv:=vv-char_status(cur_font)(p); end
  403. XX  else begin
  404. XX    update_pos;
  405. XX    write_lj(vis_chr(p));
  406. XX  end;
  407. XX  lj_h:=lj_h+char_pixel_width(cur_font)(p);
  408. XXend;
  409. XXif o>=put1 then begin
  410. XX  hh:=hh-char_pixel_width(cur_font)(p);
  411. XX  goto done;
  412. XXend;
  413. XXif q=invalid_width then q:=0
  414. XXelse hh:=hh+char_pixel_width(cur_font)(p);
  415. XXgoto move_right
  416. XX 
  417. XX@ @<Download font header@>=
  418. XXbegin
  419. XX  height.i0:=4*round(font_design_size[cur_font]*
  420. XX                     font_mag[cur_font]/1000.0*conv);
  421. XX  if (height.i0 < 0) or (height.i0>10922) then
  422. XX    height.i0:= 10922;
  423. XX  pitch.i0:=height.i0-20; {is this value used for anything in the printer??}
  424. XX  
  425. XX  write_lj(@=#27'*c'@>,font_num[cur_font]:1,
  426. XX  {char cell 255*255 pixels max}
  427. XX  {baseline distance set to $255-$|baseline| }
  428. XX    @='D'#27')s26W'#0#26#0#1#0#0#0@>, chr(255-baseline),
  429. XX    @=#0#255#0#255#0#1#1#21@>,
  430. XX    pitch.c[0], pitch.c[1], height.c[0], height.c[1],
  431. XX    @=#0#0#0#0#0#0#27'*c4F'@>);
  432. XX  incr(fonts_in_use);
  433. XXend
  434. XX  
  435. XX@ @<Delete least recently...@>=
  436. XXbegin
  437. XX  j:=9999;
  438. XX  k:=nf;
  439. XX  for i:=0 to nf-1 do
  440. XX    if font_used_on[i] < j then begin
  441. XX      j:=font_used_on[i];
  442. XX      k:=i;
  443. XX    end;
  444. XX  write_lj(@=#27'*c'@>,font_num[k]:1,'d2F');
  445. XX  font_used_on[k]:=0;
  446. XX  for i:=0 to 127 do
  447. XX    if (char_status(k)(i) > 0) or (char_status(k)(i) = loaded_ok) then
  448. XX      char_status(k)(i):=not_loaded;
  449. XX  decr(fonts_in_use);
  450. XXend
  451. XX  
  452. XX@ @<Finish a command that either sets or puts a rule...@>=
  453. XXq:=signed_quad;
  454. XXqq:=rule_pixels(q);
  455. XXpp:=rule_pixels(p);
  456. XXif (p>0) and (q>0) then begin
  457. XX  vv:=vv-pp;
  458. XX  update_pos;
  459. XX  write_lj(@=#27'*c'@>,qq:1,'a',pp:1,'b0P');
  460. XX  vv:=vv+pp;
  461. XXend;
  462. XXif o=put_rule then goto done;
  463. XXhh:=hh+qq;
  464. XXgoto move_right
  465. XX  
  466. XX@ A sequence of consecutive rules, or consecutive characters in a fixed-width
  467. XXfont whose width is not an integer number of pixels, can cause |hh| to drift
  468. XXfar away from a correctly rounded value. \.{DVIplus} ensures that the
  469. XXamount of drift will never exceed |max_drift| pixels.
  470. XX  
  471. XXSince \.{DVIplus} is intended to diagnose strange errors, it checks
  472. XXcarefully to make sure that |h| and |v| do not get out of range.
  473. XXNormal \.{DVI}-reading programs need not do this.
  474. XX  
  475. XX@d infinity==@'17777777777 {$\infty$ (approximately)}
  476. XX@d max_drift=2 {we insist that abs|(hh-pixel_round(h))<=max_drift|}
  477. XX  
  478. XX@<Finish a command that sets |h:=h+q|, then |goto done|@>=
  479. XXif (h>0)and(q>0) then if h>infinity-q then
  480. XX  begin error('arithmetic overflow! parameter changed from ',
  481. XX@.arithmetic overflow...@>
  482. XX    q:1,' to ',infinity-h:1);
  483. XX  q:=infinity-h;
  484. XX  end;
  485. XXif (h<0)and(q<0) then if -h>q+infinity then
  486. XX  begin error('arithmetic overflow! parameter changed from ',
  487. XX    q:1, ' to ',(-h)-infinity:1);
  488. XX  q:=(-h)-infinity;
  489. XX  end;
  490. XXhhh:=pixel_round(h+q);
  491. XXif abs(hhh-hh)>max_drift then begin
  492. XX  if hhh>hh then hh:=hhh-max_drift
  493. XX  else hh:=hhh+max_drift;
  494. XXend;
  495. XXh:=h+q;
  496. XXif abs(h)>max_h_so_far then
  497. XX  begin if abs(h)-round(h_offset/conv)>max_h+99 then
  498. XX    begin error('warning: h > ',max_h:1,'!');
  499. XX@.warning: |h|...@>
  500. XX    max_h:=abs(h);
  501. XX    end;
  502. XX  max_h_so_far:=abs(h);
  503. XX  end;
  504. XXgoto done
  505. XX  
  506. XX@ @<Finish a command that sets |v:=v+p|, then |goto done|@>=
  507. XXif (v>0)and(p>0) then if v>infinity-p then
  508. XX  begin error('arithmetic overflow! parameter changed from ',
  509. XX@.arithmetic overflow...@>
  510. XX    p:1,' to ',infinity-v:1);
  511. XX  p:=infinity-v;
  512. XX  end;
  513. XXif (v<0)and(p<0) then if -v>p+infinity then
  514. XX  begin error('arithmetic overflow! parameter changed from ',
  515. XX    p:1, ' to ',(-v)-infinity:1);
  516. XX  p:=(-v)-infinity;
  517. XX  end;
  518. XXvvv:=pixel_round(v+p);
  519. XXif abs(vvv-vv)>max_drift then begin
  520. XX  if vvv>vv then vv:=vvv-max_drift
  521. XX  else vv:=vvv+max_drift;
  522. XXend;
  523. XXv:=v+p;
  524. XXif abs(v)>max_v_so_far then
  525. XX  begin if abs(v)-round(v_offset/conv)>max_v+99 then
  526. XX    begin error('warning: v > ',max_v:1,'!');
  527. XX@.warning: |v|...@>
  528. XX    max_v:=abs(v);
  529. XX    end;
  530. XX  max_v_so_far:=abs(v);
  531. XX  end;
  532. XXgoto done
  533. XX  
  534. XX@ @<Finish a command that changes the current font...@>=
  535. XXbegin
  536. XX  font_num[nf]:=p; cur_font:=0;
  537. XX  while font_num[cur_font]<>p do incr(cur_font);
  538. XX  goto done
  539. XXend
  540. XX  
  541. XX@* Using the backpointers.
  542. XXFirst comes a routine that illustrates how to find the postamble quickly.
  543. XX  
  544. XX@<Find the postamble, working back from the end@>=
  545. XXn:=dvi_length;
  546. XXif n<53 then bad_dvi('only ',n:1,' bytes long');
  547. XX@.only n bytes long@>
  548. XXm:=n-4;
  549. XXrepeat if m=0 then bad_dvi('all 223s');
  550. XX@.all 223s@>
  551. XXmove_to_byte(m); k:=get_byte; decr(m);
  552. XXuntil k<>223;
  553. XXif k<>dvi_id then bad_dvi('ID byte is ',k:1);
  554. XX@.ID byte is wrong@>
  555. XXmove_to_byte(m-3); q:=signed_quad;
  556. XXif (q<0)or(q>m-33) then bad_dvi('post pointer ',q:1,' at byte ',m-3:1);
  557. XX@.post pointer is wrong@>
  558. XXmove_to_byte(q); k:=get_byte;
  559. XXif k<>post then bad_dvi('byte ',q:1,' is not post');
  560. XX@.byte n is not post@>
  561. XXpost_loc:=q; first_backpointer:=signed_quad
  562. XX  
  563. XX@ Note that the last steps of the above code save the locations of the
  564. XXthe |post| byte and the final |bop|.  We had better declare these global
  565. XXvariables, together with another one that we will need shortly.
  566. XX  
  567. XX@<Glob...@>=
  568. XX@!post_loc:integer; {byte location where the postamble begins}
  569. XX@!first_backpointer:integer; {the pointer following |post|}
  570. XX@!start_loc:integer; {byte location of the first page to process}
  571. XX@!start_inx:integer; {index into |page_start| for first page}
  572. XX@!last_loc:integer; {byte localtion of last page to process}
  573. XX@!page_start:array[1..max_bops] of integer; {pointers to |bop|s}
  574. XX  
  575. XX@ The next routines follow the backpointers
  576. XXto move through a \.{DVI} file in reverse order. \.{DVIplus} does this because
  577. XXit wants to print the pages backwards as the LaserJet stacks
  578. XXthem with the printed side up.
  579. XX 
  580. XXFirst we search for the starting page and the last page.
  581. XX  
  582. XX@<Scan for page range to print@>=
  583. XXq:=post_loc; p:=first_backpointer; start_loc:=-1;
  584. XXrepeat
  585. XX  {now |q| points to a |post| or |bop| command; |p>=0| is prev pointer}
  586. XX  if p>q-46 then
  587. XX    bad_dvi('page link ',p:1,' after byte ',q:1);
  588. XX@.page link wrong...@>
  589. XX  q:=p; move_to_byte(q);
  590. XX  k:=get_byte;
  591. XX  if k=bop then incr(page_count)
  592. XX  else bad_dvi('byte ',q:1,' is not bop');
  593. XX@.byte n is not bop@>
  594. XX  if page_count>max_bops then bad_dvi('there are too many pages');
  595. XX@.there are too many pages@>
  596. XX  page_start[page_count]:=q;
  597. XX  for k:=0 to 9 do count[k]:=signed_quad;
  598. XX  if start_match then begin start_loc:=q; start_inx:=page_count; end;
  599. XX  p:=signed_quad;
  600. XXuntil p<0;
  601. XXif start_loc<0 then abort('starting page number could not be found!');
  602. XX@.starting page number...@>
  603. XXif (start_inx > max_pages) then
  604. XX  last_loc:=page_start[start_inx-max_pages+1]
  605. XXelse
  606. XX  last_loc:=page_start[1];
  607. XXif page_count<>total_pages then
  608. XX  print_ln('there are really ',page_count:1,
  609. XX    ' pages, not ',total_pages:1,'!');
  610. XX@.there are really n pages@>
  611. XX 
  612. XX@ This is the code that really goes through the pages to be printed
  613. XX(in reverse order). It starts from the page pointed to by |last_loc| and
  614. XXproceeds up to |start_loc|.
  615. XX  
  616. XXThe code shown here uses a convention that has proved to be useful:
  617. XXIf the starting page was specified as, e.g., `\.{1.*.-5}', then
  618. XXall page numbers in the file are displayed by showing the values of
  619. XXcounts 0, 1, and~2, separated by dots. Such numbers can, for example,
  620. XXbe displayed on the console of a printer when it is working on that
  621. XXpage.
  622. XX  
  623. XX@<Translate the page range in reverse order@>=
  624. XXpage_count:=0;
  625. XXq:=last_loc;
  626. XXrepeat
  627. XX  move_to_byte(q); k:=get_byte;
  628. XX  incr(page_count);
  629. XX  fonts_on_page:=0;
  630. XX  for k:=0 to 9 do count[k]:=signed_quad;
  631. XX  q:=signed_quad;
  632. XX  print('[');
  633. XX  for k:=0 to start_vals do
  634. XX    begin print(count[k]:1);
  635. XX      if k<start_vals then print('.');
  636. XX    end;
  637. XX  update_terminal;
  638. XX  do_page;
  639. XX  print('] ');
  640. XX  update_terminal;
  641. XXuntil q<start_loc;
  642. XX  
  643. XX@* Reading the postamble.
  644. XXNow imagine that we are reading the \.{DVI} file and positioned just
  645. XXfour bytes after the |post| command. That, in fact, is the situation,
  646. XXwhen the following part of \.{DVIplus} is called upon to read, translate,
  647. XXand check the rest of the postamble.
  648. XX  
  649. XX@p procedure read_postamble;
  650. XXvar k:integer; {loop index}
  651. XX@!p,@!q,@!m:integer; {general purpose registers}
  652. XXbegin post_loc:=cur_loc-5;
  653. XX@.Postamble starts at byte n@>
  654. XXif signed_quad<>numerator then
  655. XX  print_ln('numerator doesn''t match the preamble!');
  656. XX@.numerator doesn't match@>
  657. XXif signed_quad<>denominator then
  658. XX  print_ln('denominator doesn''t match the preamble!');
  659. XX@.denominator doesn't match@>
  660. XXif signed_quad<>mag then if new_mag=0 then
  661. XX  print_ln('magnification doesn''t match the preamble!');
  662. XX@.magnification doesn't match@>
  663. XXmax_v:=signed_quad; max_h:=signed_quad;@/
  664. XXmax_s:=get_two_bytes; total_pages:=get_two_bytes;@/
  665. XXif total_pages>max_bops then
  666. XX  bad_dvi('enormous number of pages (', total_pages:1, ')');
  667. XX@.enormous number of pages@>
  668. XX@<Process the font definitions of the postamble@>;
  669. XX@<Make sure that the end of the file is well-formed@>;
  670. XXend;
  671. XX  
  672. XX@ No warning is given when |max_h_so_far| exceeds |max_h| by less than~100,
  673. XXsince 100 units is invisibly small; it's approximately the wavelength of
  674. XXvisible light, in the case of \TeX\ output. Rounding errors can be expected
  675. XXto make |h| and |v| slightly more than |max_h| and |max_v|, every once in
  676. XXa~while; hence small discrepancies are not cause for alarm.
  677. XX  
  678. XX@ When we get to the present code, the |post_post| command has
  679. XXjust been read.
  680.  
  681. XX@<Make sure that the end of the file is well-formed@>=
  682. XXq:=signed_quad;
  683. XXif q<>post_loc then
  684. XX  print_ln('bad postamble pointer in byte ',cur_loc-4:1,'!');
  685. XX@.bad postamble pointer@>
  686. XXm:=get_byte;
  687. XXif m<>dvi_id then print_ln('identification in byte ',cur_loc-1:1,
  688. XX@.identification...should be n@>
  689. XX    ' should be ',dvi_id:1,'!');
  690. XXk:=cur_loc; m:=223;
  691. XXwhile (m=223)and not eof_dvi_file do m:=get_byte;
  692. XXif not eof_dvi_file then bad_dvi('signature in byte ',cur_loc-1:1,
  693. XX@.signature...should be...@>
  694. XX    ' should be 223')
  695. XXelse if cur_loc<k+4 then
  696. XX  print_ln('not enough signature bytes at end of file (',
  697. XX@.not enough signature bytes...@>
  698. XX    cur_loc-k:1,')');
  699.  
  700. XX@ @<Process the font definitions...@>=
  701. XXrepeat k:=get_byte;
  702. XXif (k>=fnt_def1)and(k<fnt_def1+4) then
  703. XX  begin p:=first_par(k); define_font(p); print_nl; k:=nop;
  704. XX  end;
  705. XXuntil k<>nop;
  706. XXif k<>post_post then
  707. XX  print_ln('byte ',cur_loc-1:1,' is not postpost!')
  708. XX@.byte n is not postpost@>
  709. XX  
  710. XX@* The main program.
  711. XXNow we are ready to put it all together. This is where \.{DVIplus} starts,
  712. XXand where it ends.
  713. XX  
  714. XX@p begin dont_catch_errors; initialize; {get all variables initialized}
  715. XXdialog; {set up all the options}
  716. XX@<Process the preamble@>;
  717. XX@<Find the postamble, working back from the end@>;
  718. XXread_postamble;
  719. XXprint_ln('Total of ', nf:1, ' fonts.');
  720. XX@<Scan for page range to print@>;
  721. XXwrite_lj(@=#27'E'#27'&l'@>, copies:1, @='X'#27'*t300R'@>);
  722. XX@<Translate the page range...@>;
  723. XXwrite_lj(@=#27'&l1X'@>);
  724. XXwrite_ln(laser_file,'_');
  725. XXprint_nl
  726. XXend.
  727. XX  
  728. XX@ The main program needs a few global variables in order to do its work.
  729. XX  
  730. XX@<Glob...@>=
  731. XX@!k,@!m,@!n,@!p,@!q:integer; {general purpose registers}
  732. XX  
  733. XX@ A \.{DVI}-reading program that reads the postamble first need not look at the
  734. XXpreamble; but \.{DVIplus} looks at the preamble in order to do error
  735. XXchecking, and to display the introductory comment.
  736. XX  
  737. XX@<Process the preamble@>=
  738. XXif not open_dvi_file then
  739. XX  bad_dvi('cannot open file');
  740. XX@<Open and lock |laser_file|@>;
  741. XXp:=get_byte; {fetch the first byte}
  742. XXif p<>pre then bad_dvi('first byte isn''t start of preamble!');
  743. XX@.First byte isn't...@>
  744. XXp:=get_byte; {fetch the identification byte}
  745. XXif p<>dvi_id then
  746. XX  print_ln('identification in byte 1 should be ',dvi_id:1,'!');
  747. XX@.identification...should be n@>
  748. XX@<Compute the conversion factor@>;
  749. XXp:=get_byte; {fetch the length of the introductory comment}
  750. XXprint('''');
  751. XXwhile p>0 do
  752. XX  begin decr(p); print(xchr[get_byte]);
  753. XX  end;
  754. XXprint_ln('''')
  755.  
  756. XX@ The conversion factor |conv| is figured as follows: There are exactly
  757. XX|n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch,
  758. XXand |resolution| pixels per inch. Then we have to adjust this
  759. XXby the stated amount of magnification.
  760. XX  
  761. XX@<Compute the conversion factor@>=
  762. XXnumerator:=signed_quad; denominator:=signed_quad;
  763. XXif numerator<=0 then bad_dvi('numerator is ',numerator:1);
  764. XX@.numerator is wrong@>
  765. XXif denominator<=0 then bad_dvi('denominator is ',denominator:1);
  766. XX@.denominator is wrong@>
  767. XXconv:=(numerator/254000.0)*(resolution/denominator);
  768. XXmag:=signed_quad;
  769. XXif new_mag>0 then mag:=new_mag
  770. XXelse if mag<=0 then bad_dvi('magnification is ',mag:1);
  771. XX@.magnification is wrong@>
  772. XXtrue_conv:=conv; conv:=true_conv*(mag/1000.0);
  773. XX  
  774. XX@* System-dependent changes.
  775. XXThis section should be replaced, if necessary, by changes to the program
  776. XXthat are necessary to make \.{DVIplus} work at a particular installation.
  777. XXIt is usually best to design your change file so that all changes to
  778. XXprevious sections preserve the section numbering; then everybody's version
  779. XXwill be consistent with the printed program. More extensive changes,
  780. XXwhich introduce new sections, can be inserted here; then only the index
  781. XXitself will get a new section number.
  782. XX@^system dependencies@>
  783.  
  784. XX@* Index.
  785. XXPointers to error messages appear here together with the section numbers
  786. XXwhere each ident\-i\-fier is used.
  787. XX-------------------------------------- end of last part ----------------
  788.  
  789.  
  790. @//E*O*F LJ.3.3//
  791. chmod u=rw,g=rw,o=rw LJ.3.3
  792.  
  793. exit 0
  794.