summaryrefslogtreecommitdiff
path: root/dviware/crudetype/version3/hp.ch
blob: 3ffd6863e9d2b712ce457b7d7df144794a80e93d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
% hp.ch -*-mode: change; webfile: crudetype.web version 3.01;-*-
% HP.CH  Provisional change file for the HP Laserjet...
% NOTE the system change file must normally be inserted above this point.
% This file reads PXL files, so cannot be tested at our site
%

@x  Module 41; Lines 822 -- 839
When this module starts, the \.{DVI} file should be positioned at or before a
BOP.

@<For each page...@>=
  read_BOP;
  if (counter[0] >= first_page) then start := true ;
  if start and (count_pages > 0 )
  then begin
    @<Maybe a formfeed@>
    decr(count_pages);
    if not quiet then display('[', counter[0]:1 ); {Progress report}
    Read_one_page ;
    @<Sort the page@>
    Send_page ;
    if not quiet then display( ']' );
  end
  else if ( count_pages > 0) then Skip_page
  else time_to_stop := true;
@y
When this module starts, the \.{DVI} file should be positioned at or before a
BOP.

This is where the printer change file proper begins. This change file goes
with \.{Crudetype} version 2. First, it should be explained that the HP is not
at all a "crude" printer, and the mechanisms of \.{Crudetype} are not really
suitable for it. It is really stretching the program a very long way from its
intended purpose. In particular, some changes have to be spliced into the
middle of the program, instead of going at the end as printer changes ought
to. It seems that the only reasonable way to drive a HP is by downloading all
the required characters. As stated in \.{Crudetype}, the problems of
downloading are extremely difficult and I have not solved them in any
satisfactory manner. The code given below manages downloading in the simplest
and crudest way possible.

First, I have added flags to print either even or odd pages only. In
principle, this will allow double sided printing. Also, we do not sort the
page as the HP can jump about.

@<For each page...@>=
  read_BOP;
  if (counter[0] >= first_page) then start := true ;
  if start and (count_pages > 0 ) and (
    ( odd( counter[0]) = odds) or (( not evens) and ( not odds)) )
  then begin
    decr(count_pages);
    if not quiet then display('[', counter[0]:1 ); {Progress report}
    Read_one_page ;
    @<Dont sort the page but |reset| it @>
    Send_page ;
    @<Formfeed@>;
    if not quiet then display( ']' );
  end
  else if ( count_pages > 0) then Skip_page
  else time_to_stop := true;
@z

% Next, the HP has its own rule-setting commands.
@x  Module 57; Lines 1126 -- 1136
  procedure set_rule;
  var D_p,D_q: integer;
  begin
    D_p:=get_integer(dvi) (-4);
    D_q:=get_integer(dvi)(-4);
    if (D_p<=0)or(D_q<=0) then
      {an invisible rule! Dont ask me why \TeX\ wants to do this}
    else if (D_p*v_conv <= post_height/2)
    then do_rail(D_p, D_q)
    else do_post(D_p, D_q);
  end;
@y
  procedure set_rule;
  var D_p,D_q: integer;
  rule_h, rule_v, rule_ht, rule_wid: integer ; {all in pixels}
  begin
    D_p:=get_integer(dvi) (-4);
    D_q:=get_integer(dvi)(-4);
    if (D_p<=0)or(D_q<=0) then
    {an invisible rule! Dont ask me why \TeX\ wants to do this}
    else begin @<Find the pixel sizes and reference point@>
      @<Send it to the printer@>
    end;
  end;
@z

@x  Module 174; Lines 2958 -- 2959
@d out_of_sequence ==
  ( ( Old_v > Set_v) or ( ( Old_v = Set_v) and ( Old_h > Set_h)))
@y
Since we do not sort, we will not separate the characters into runs.

@d out_of_sequence == false
@z

@x  Module 206; Lines 3419 -- 3457
@* Default declarations for printer.

This section defines masses of data to describe how the printer behaves.
Previously this was all in the lineprinter change file. However most Change
files are for lineprinters, so I moved this stuff into the main program. In V3 I
changed most of these constants into variables.  The assumed characteristics of
a lineprinter are as follows:

1. A lineprinter can print all the printable ASCII characters, and no others.

2. Each character is one |step| high and one |step| wide.

3. Printer will act correctly if it receives the following ASCII controls:
line feed, carriage return, space, and form feed. More precisely, the Standard
specifies that the \PASCAL\ procedure |page| does something that advances
the printer by one page.

4. Backspacing and backfeeding are assumed impossible ; also we do not use
tabs.

The first lot of data describes the printer's overall style of carriage control.
|fortran| means that the carriage control character gets put at the start of the
line, and it is here assumed that it must be inserted explicitly.  |feed| means
a vertical movement and |space| horizontal.  Each |thing_char| is the character
needed to make the printer do the named action. Owing to the rules of
\.{TANGLE}, the words |back| and |tiny| have to be abbreviated (to avoid
identifier clashes).
@.ASCII@>

@<Glob...@>=
  @!device_ID : packed array[1..12] of char ; {Name of device}
@#
  list, fortran, b_feed_absolute, b_feed_by_string, feed_absolute,
  b_feed_scream, b_space_absolute, b_space_by_string, space_absolute,
  abs_is_incr, wl_does_cr, want_split, is_header, do_pause, inspection: boolean ;
  {These say whether the printer can do the named action}
@#
  wl_feed_dist, cr_feed_dist, feed_dist, t_feed_dist, b_feed_dist,
  tiny_drop, big_drop, space_dist, t_space_dist, b_space_dist,
@y
@* Printer dependent data.

This section defines masses of data to describe how the printer behaves.
Previously this was all in the Line printer change file. However most Change
files are for lineprinters, so I moved this stuff into the main program. The
assumed characteristics of a lineprinter are as follows:

1. A lineprinter can print all the printable ASCII characters, and no others.

2. Each character is one |step| high and one |step| wide.

3. Printer will act correctly if it receives the following ASCII controls:
line feed, carriage return, space, and form feed. More precisely, the Standard
specifies that the \PASCAL\ procedure |page| does something that advances
the printer by one page.

4. Backspacing and backfeeding are assumed impossible ; also we do not use
tabs.

@ The first lot of data describes the HP's overall style of carriage
control. Many of them are completely irrelevant to the HP, but still needed in
order for the program to compile.

@<Set init...@>=
  device_ID := 'Laserjet +  '; {Pad to 12 chars}
  list := false ;
  b_feed_absolute := true ;
  b_feed_by_string := false ;
  feed_absolute := true ;
  b_feed_scream := true ;
  b_space_absolute := true ;
  b_space_by_string :=false ;
  space_absolute := true ;
  space_absolute := false ;
  abs_is_incr := false ;
  w_l_does_c_r := false ;
  want_split := true ;
  is_header := false ; {each page needs a header}
@z

@x  Module 207; Lines 3472 -- 3472
  device_ID := 'Lineprinter '; {Pad to 12 chars}
@y
  device_ID := 'Laserjet +  ';
@z

@x  Module 208; Lines 3518 -- 3527
@ The general run of \TeX\ characters are narrower than line-printer chars. So
we spread them out to make them fit.

@<Set init...@>=
  l_margin := 1.0 ; {Normal left margin, in inches}
  top_margin := 1.0 ; {Top ditto}
  h_fudge := 7.227 {number of points per |h_step|}
  / 5.25 ; {A typical design width}
  v_fudge := 2.0 ;
  { Force double-spacing, in hope that suffixes will come out right}
@y
@ The general run of \TeX\ characters are narrower than line-printer chars.
But the HP prints them at their proper widths.

@<Set init...@>=
  l_margin := 1.0 ; {Normal left margin, in inches}
  top_margin := 1.0 ; {Top ditto}
  h_fudge := 1.0 ;
  v_fudge := 1.0 ;
@z

@x  Module 213; Lines 3587 -- 3594
  h_resolution = 10 ;         {|h_steps| per inch}
  v_resolution = 6 ;          {|v_steps| per inch}
  fixed_width = true ;        {printers characters are fixed width}
  char_width = 1 ;
  {all printer characters are this width, in units of |h_step|. Normally,
    |space_dist| will be equal to this, but some printers are not normal!}
  gap_width = 1 ; {Intended minimum space between words}
  char_ht = 1 ;
@y
  h_resolution = 300 ;         {|h_steps| per inch}
  v_resolution = 300 ;         {|v_steps| per inch}
  fixed_width = false ;
  char_width = 30 ;    {default char. sizes in |h_steps| -- a guess}
  gap_width = 5 ; {Intended minimum space between words}
  char_ht = 42 ;
@z

@x  Module 216; Lines 3611 -- 3623
@ The next batch are concerned with fonts.

@<Const...@>=
  min_font = 1 ;
  {smallest and largest number of printers resident fonts}
  max_font = 1 ;
  only_one_font = true ;
  can_dl_font = false ;
  min_dl_font = 0 ;
  max_dl_font = 0 ; {printers down-loadable fonts}
  max_codes = 60 ; {no. of known \TeX\ coding schemes}
  max_plain = 4 ;  {Max number of a plain text font}

@y
@ The next batch are concerned with fonts.

@<Const...@>=
  min_font = 1 ;
  {smallest & largest number of printers resident fonts}
  max_font = 40 ;
  only_one_font = false ;
  can_dl_font = true ;
  min_dl_font = 8 ;
  max_dl_font =  40 ; {printers down-loadable fonts. The HP allows up to 32}
  max_codes = 40 ; {no. of possible \TeX\ coding schemes}
  max_plain = 3 ;  {Max number of a plain text font}
@z

@x  Module 234; Lines 4022 -- 4024
@<Assign char...@>=
  @<Define Lineprinter codes@>
  @<Set rule characters@>
@y
  @<Assign char...@>= do_nothing
@z

@x  Module 235; Lines 4028 -- 4028
*** Attach printer change file here ***
@y

@ The remaining changes can all go at the end of the program. Before getting
onto the hardest task (namely, downloading) lets clear up the loose ends that
were left lying about in the body of the program. First, there are a number of
extra command options:

@<If the |key|...@>=
  else if ( key = "O") then odds := true  {Print odd-numbered pages only}
  else if ( key = "E") then evens := true {Even ditto}
  else if ( key = "L") then begin
    land := true ;  {Print Landscape}
    start_stuff.data[ 6] := '1' ;
  end

@ @<Glob...@>=
  land, odds, evens: boolean ;

@ @<Set init...@>=
  land := false ;
  odds := false ;
  evens := false ;

@ Where will the printed file go to?

@<Set init...@>=
  be_string( '.HPL' ) ;  print_ex := buffer ;

@ @<Dont sort the page but |reset| it @>=
  L_reset( run) ;
  Add_run ;
  L_reset( mid) ;
  page_ptr := son( next( mid) ) ;

@ Now lets dispose of rule-setting. \TeX\ puts the reference point of a rule
at bottom left, the HP at top left. Sizes must be rounded up.

@<Find the pixel sizes and reference point@>=
  rule_ht := round(v_conv*D_p + 0.5) ;
  rule_wid := round(h_conv*D_q + 0.5) ;
  D_dis := D_q ;
  IM_dis := rule_wid ;
  round_IM_h ( 0);
  rule_h := IM_h ;
  rule_v := IM_v - rule_ht ;

@ @<Send it to the printer@>=
  set_v_abs(rule_v) ;
  set_h_abs(rule_h) ;
  print(chr(27), '*c', rule_ht:1, 'B') ;
  print(chr(27), '*c', rule_wid:1, 'A') ;
  print(chr(27), '*c0P') ;
  print_ln;

@ Consider command strings.

@<Set init...@>=
  be_string ( '^[E^[&l0O' ) ; start_stuff := buffer ;
    {Reset everything to default state}
  be_string ( '^[(&DX' ) ; font_command := buffer ;
  be_string ( '^[*p&DY' ) ; v_abs_com := buffer ;
  be_string ( '^[*p&DX ' ) ; h_abs_com := buffer ;
  stop_stuff := start_stuff ;
  page_top := blank ;
  pause_after := blank ;

@ On the HP, we must explicitly start a new page at a set position. Also since
rules get set before any characters, we must then reset the position.

@<Set up an empty page image@>=
  set_v_abs(0) ;
  set_h_abs(0) ;

@ @<Pause reset@>=
  set_v_abs(0) ;
  set_h_abs(0) ;

@* Downloading a font.

The simplest and crudest way this could possibly be done is: read the \.{PXL}
file and load the entire font, as soon as the |font_def| command is read from
the \.{DVI} file. On VAX/VMS, this turned out to be unbearably slow. So it is
here changed as follows: When a |font_def| command is read, we read the whole
pixel file into a  very large array. Then download each character before
trying to print it. This `lazy downloading' makes the program run much faster,
at the price of an enormous |pixels| array. See \.{TUG}boat (Vol.2, No.3) for
a description of the \.{PXL} file format.
 @^\.{TUG}boat@>

@<Download a whole font@>=
  begin
    @<Prepare to read the |raster_file|@>
    @<Read the pixel file into |pixels|, checking for errors @>
    @<Get the overall font parameters and send font header@>
    @<Make the coding scheme point to the new font@>
    goto good_font; {Bypass the rest of the \.{TFM} file }
  end

@ First we have to determine the file name.

@<Prepare to read...@>=
  raster_mag := round( 1500 * font_mag *  magnification ) ;
  if not hunt_for_size( font_name, raster_mag)
  then font_error('cannot load this font') ;
    @.Error: cannot load@>

@ @<Medium...@>=
  function open_font(
    name: var_string; mag: integer; ask: boolean ): boolean;
  begin
    splice( raster_name, raster_def, mag) ;
    open_font := open_and_ask(
      raster_file, raster_indx, name, raster_name, ask) ;
  end;

@ @<Set init...@>=
   be_string( 'TEX$PXL:.&DPXL' ) ;  raster_def := buffer ;

@ Frequently the {\.DVI} file calls for a font at a magnification that is
almost but not quite one of the standard sizes. So we try a few steps up or
down before giving up. |range| is the maximum percentage that we allow the
magnification to vary.

@<Forw...@>=
  function hunt_for_size (
    name: var_string; mag: integer): boolean; forward ;

@ @<Medium...@>= function hunt_for_size ;
  label exit ;
  const range = 5 ;
  var try_mag, n , max : integer; hh: boolean;
  begin
    max := round( raster_mag* range / 100);
    n := 0 ;
    while ( n <= max) do begin
      try_mag := mag + n ;
      hh := open_font( name, try_mag, false) ;
      if hh then return
      else if ( n>0) then n:= -n
      else n := 1 - n ;
    end;
    hh := open_font( name, mag, true) ;
    exit: hunt_for_size := hh;
  end;

@ Then we actually read the file into an array called |pixels|. In VMS, the
short block at the end of the file ought to be padded with zeroes, but
actually seems to be full of garbage. So we must do a fudge to find the true
end of the file.

@<Read the pixel...@>=
  pxl_start [ nf] := pxl_end ;
  repeat
    pixels[ pxl_end] := get_byte( raster) ;
    incr( pxl_end);
    if pxl_end = max_pixels then
    font_error('overflowed pixel array');
      @.error: font: overflowed pixel array@>
  until eof(raster_file) ;
  while (pixels[ pxl_end] <> pixels[ pxl_start[ nf]+3]) or
  (pixels[ pxl_end-1] <> pixels[ pxl_start[ nf]+2]) or
  (pixels[ pxl_end-2] <> pixels[ pxl_start[ nf]+1]) or
  (pixels[ pxl_end-3] <> pixels[ pxl_start[ nf]])
  do decr(pxl_end) ;
  close_binary(raster_file) ;
    @.System dependencies@>

@ |pxl_end| is the highest used point in |pixels|; the fonts will be piled in
on top of one another and |pxl_start[n]| points to the start of font n .

@<Glob...@>=
  pixels: packed array[1..max_pixels] of byte ;
  pxl_mag, pxl_check, pxl_end, dir_ptr : integer;
  pxl_start, dir_start: array [D_font_ptr] of integer ;
  raster_mag: integer;

@ @<Set init...@>= pxl_end := 1 ;

@ @<Const...@>= max_pixels = 1000000 ;

@ @<|font_def| vars@>=
  font_i, tex_chr : byte ;

@ This is of course a guess, and perhaps we may want to refine it.

@<Clean...@>=
  display_ln
    ('Used ', pxl_end:1, ' bytes of pixel memory out of ', max_pixels:1) ;

@ Now |pxl_end| should be pointing to the very last byte of the file; so we
will do some checking. |next_half| gets a 16-bit half word from the file, and
|two_comp| negates it in twos-complement.

@d next_half ==
  pixels[dir_ptr] * 256 + pixels[dir_ptr+1] ;
  dir_ptr := dir_ptr +2

@d two_comp(#) == if # > 32767 then # := # - 65536 ;

@<Read the pixel...@>=
  if pxl_end < 2000 + pxl_start[ nf] then
  font_error('Pixel file truncated!');
  dir_ptr := pxl_end - 19 ;
  pxl_check := next_half ;
  two_comp(pxl_check) ;
  pxl_check := pxl_check * 65536 + next_half ;
  if pxl_check <> TFM_check then begin
    warn('pixel checksum disagrees with TFM checksum') ;
    display('pixel sum is', pxl_check) ;
  end;
    @.error: pixel checksum@>
    @.error: font: Pixel file truncated@>

@ Next, look at the directory pointer.

@<Read the pixel...@>=
  dir_ptr := pxl_end - 7 ;
  dir_start[ nf] := next_half ;
  dir_start[ nf] := dir_start[ nf] * 65536 + next_half ;
  dir_start[ nf] := dir_start[ nf] * 4 +  pxl_start[ nf] ;
    {we count bytes, not words}
  if dir_start[ nf] + 2067 <> pxl_end then
  warn('pixel directory pointer disagrees with file size') ;
    @.Error: pixel directory pointer@>

@ Finally, does the printer have enough room for the font? The HP allows 32
fonts per job and 395 KB memory. I have not checked the restriction of only 16
fonts per page.

@<Read the pixel...@>=
  incr(PR_dl_font ) ;
  if PR_dl_font > max_dl_font then
  font_error('tried to load too many fonts') ;
    @.Error: font: tried to load@>
    @.Error: font: overflowed printer memory@>

@ @<Set init...@>= PR_dl_font := min_dl_font ;
  PR_mem_used := 0 ;
  PR_max_mem := 395000;

@ @<Glob...@>= PR_dl_font, PR_max_mem, PR_mem_used: integer ;

@ If the error tests succeed, then we come here. Before we can load any
characters, we have to send a command to the printer to declare the new font.
This section assembles the necessary information. |dir_start[ nf]| should be
pointing to the start of the font directory. The main task is that the printer
must be given the size of a character cell; this must be large enough to
contain all the characters.

@<Get the overall font...@>=
  cell_bot := 0 ;
  cell_top := 0 ;
  cell_wid := 0 ;
  dir_ptr := dir_start[ nf] ;
  for tex_chr := 0 to 127 do begin
    @<Stretch the cell to make the character fit inside, and adjust the
      characters width@>
  end;
  cell_ht := cell_bot + cell_top ;  {FUDGE}

@ @<Stretch...@>=
  C_pxl_w := next_half ;
  C_pxl_h := next_half ;
  C_h_off := next_half ;
  C_v_off := next_half ;
  two_comp(C_h_off) ;
  two_comp(C_v_off) ;
  if C_h_off < 0 then C_pxl_w := C_pxl_w - C_h_off ;
  if C_pxl_w > cell_wid then cell_wid  := C_pxl_w ;
  if C_v_off > cell_top then cell_top  := C_v_off ;
  if C_v_off < 0 then C_pxl_h := C_pxl_h - C_v_off ;
  if C_pxl_h > cell_bot then cell_bot := C_pxl_h ;

@ @<Glob...@>=
  C_width, cell_ht, cell_top, cell_bot, cell_wid, C_pxl_w, C_pxl_h,
  C_h_off, C_v_off : integer;

@ Next, consider the character's width. This must be calculated exactly as in
\.{DVItype}, for the reasons given there.

@<Stretch...@>=
  dir_ptr := dir_ptr + 4 ;
  b0 := pixels[ dir_ptr ]; b1 := pixels[ dir_ptr + 1];
  b2 := pixels[ dir_ptr + 2]; b3 := pixels[ dir_ptr + 3];
  dir_ptr := dir_ptr + 4 ;
  C_width:= (((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
  if  b0 = 255 then C_width:=C_width - alpha
  else if b0 <> 0 then
  font_error('Out-of-bounds value for b0') ;
    @.error: font: Out-of-bounds |b0|@>
  D_width[ nf, tex_chr] := C_width ;

@ We can now send stuff to declare the font. First we specify the font ID.
This is a number by which the printer will refer to the font after loading it.

@<Get the overall font...@>=
  print(chr(27), '*c', PR_dl_font:1, 'D');
    {Specify printers font identifier}
  print(chr(27), ')s26W' );
  {Start a create font command}
  prw(26);
  prw(1) ;  { 8 bit chars}
  prw(0) ;
  prw(cell_top) ;
  prw(cell_wid) ;
  prw(cell_ht ) ;
  prw(1) ;  { portrait, proportional spaced}
  prw(277) ;
  for font_i := 1 to 5 do prw(0) ;
  {The HP needs these parameters, but they dont serve any purpose known to me}

@ Finally, we must establish the map from \TeX\ characters to printers
characters in the new font.

@<Make the coding scheme point to the new font@>=
  incr(top_code) ;   {get a new coding scheme}
  scheme[ nf] := top_code ;
  alphabet(0, 33, top_code, PR_dl_font, 190);
  alphabet(33, 95, top_code, PR_dl_font, 33);
  for tex_chr := 0 to max_D_char do
  codes[ top_code, tex_chr].breadth := down_loaded ;

@ @<Glob...@>= top_code: integer ;

@ @<Set init...@>= top_code := 1 ;

@* Downloading, part2: Lazy downloading.

The idea is to load only those characters in each font that actually will be
printed. It is obviously essential to ensure that each character gets loaded
before being printed, and only once. This is done in the procedure
|set_character|.  The \TeX\ character is number |c_num| in font |D_font|,
but the printer character is addressed by |cod|.

The first job is to assemble the size parameters for the character.

@<Enter a download...@>=
  begin
    dir_ptr := dir_start[D_font] + c_num * 16 ;
    C_pxl_w := next_half ;
    C_pxl_h := next_half ;
    C_h_off := next_half ;
    C_v_off := next_half ;
    two_comp(C_h_off) ;
    two_comp(C_v_off) ;
    pxl_ptr := next_half ;
    pxl_ptr := pxl_ptr * 65536 + next_half ;
    pxl_ptr := pxl_ptr * 4 + pxl_start[D_font] ;
    Tex_bytes := (C_pxl_w + 31) div 32 ;
    Tex_bytes := Tex_bytes * 4 ;      {Bytes per row in PXL file}
    PR_bytes := (C_pxl_w + 7) div 8 ;{Ditto, in HP fonts}
    C_length := PR_bytes * C_pxl_h + 16 ;

@ @<Glob...@>=
  pxl_ptr, Tex_bytes, PR_bytes, C_length, C_delta : integer;

@ Now we must not let the character get downloaded twice, so we put the
correct value into its |breadth|; we must also update the current |cod|.

@<Enter a download...@>=
  C_width := D_width[ D_font, c_num] ;
  C_delta := round(C_width * h_conv) ;
  codes[ cur_scheme, c_num].breadth := C_delta ;
  cod.breadth := C_delta ;
  PR_mem_used := PR_mem_used + C_length + 64 ;   {approximate}
  if PR_mem_used  > PR_max_mem then
  warn('overflowed printer memory, will try to proceed regardless') ;
    @.Error: overflowed printer memory@>

@ Now we send the character header. First, tell the printer which character
will be downloaded. \TeX\ fonts usually have 128 characters and HP fonts have
either 96 or 192. The permitted values for HP characters are 33..127 and
160..255 according to the manual but appendix B says 160 and some others are
undefined. So we map \TeX\ characters 0..32 onto 190..222 .

@<Enter a download...@>=
  print(chr(27), '*c', cod.IM_font:1, 'D');
    {Specify printers font identifier}
  print(chr(27), '*c') ;
  print(cod.IM_char:1 ) ;
  print('E');
  print(chr(27), '(s', C_length:1, 'W' ) ;
  prw(1024);
  prw(14*256 + 1);
  prw(0);
  prw(- C_h_off) ; {\TeX\ and the HP measure this in opposite directions}
  prw(C_v_off);
  prw(C_pxl_w) ;
  prw(C_pxl_h) ;
  prw(4 * C_delta) ;

@ And at long last we can send the pixels!! This is the only pleasant part of
the whole messy business. We need not shuffle bits because the H-P wants them
in almost the exact order that they appear in the PXL file. Only difference is
that H-P packs with zero bits to the next 8-bit byte, while PXL files pack to
next 32 bits. That is the reason for the odd-loking calculation of |Tex_bytes|
above.

@<Enter a download...@>=
  for d_i:= 1 to C_pxl_h do begin
    for d_j:= 1 to PR_bytes do begin
      print(chr(pixels[ pxl_ptr ] )) ;
      incr(pxl_ptr) ;
    end ;
    pxl_ptr := pxl_ptr + Tex_bytes - PR_bytes ;
  end;
  print_ln ;
end;

@ Nearly all the HP's arguments come as signed 16-bit words, to be printed in
two-complement notation. This procedure prints them.

@<Low...@>=
  procedure prw( n: i_word);
  var nn: integer ;
  begin
    if (n>= 0) then nn := n
    else nn := n + 65536 ;
    print( zchr(nn div 256));
    print( zchr(nn mod 256));
  end ;
@z