summaryrefslogtreecommitdiff
path: root/dviware/crudetype/version3/hpgf.ch
blob: c1a5734d2cf778a4b867bb4866d548886df38418 (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
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
% hpgf.ch -*-mode: change; webfile: crudetype.web version 3.01;-*-
% HPGF.CH  Provisional change file for the HP Laserjet...
% NOTE the system change file must normally be inserted above this point.
% SEE   HPGF.DOC   for users document--which must be rewritten for any new
% system.
% BUGFIX 19-dec-1990 Bug spoilt Landscape mode.
%

@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 ;
    @<Maybe a 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

% Dont shunt on a fine printer
@x  Module 154; Lines 2678 -- 2679
  if H_shunt > (Set_h - 3)
  then H_shunt :=  (Set_h - 3) ;
@y
  do_nothing ;
@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

% Again, shunting is only appropriate for line printers.
@x  Module 186; Lines 3157 -- 3157
      PR_h_next := hpos - H_shunt ;
@y
      PR_h_next := hpos ;
@z

@x  Module 207; Lines 3467 -- 3484
@ So here are their default values. We believe they are all appropriate for
lineprinters on VMS machines. Note that the program makes no attempt to check
these values for consistency.

@<Set init...@>=
  device_ID := 'Lineprinter '; {Pad to 12 chars}
  list := false ;
  fortran := false ;
  b_feed_absolute := false ;
  b_feed_by_string := false ;
  feed_absolute := false ;
  b_feed_scream := true ;
  b_space_absolute := false ;
  b_space_by_string := false ;
  space_absolute := false ;
  abs_is_incr := false ;
  wl_does_cr := false ;
  want_split := true ;
@y
@ 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 +  ';
  list := false ;
  fortran := 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 ;
  abs_is_incr := false ;
  wl_does_cr := false ;
  want_split := true ;
  is_header := false ; {each page needs a header}
@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 3584 -- 3594
@ This batch is concerned with distances and resolutions.

@<Const...@>=
  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
@ This batch is concerned with distances and resolutions.

@<Const...@>=
  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 3616 -- 3620
  max_font = 1 ;
  only_one_font = true ;
  can_dl_font = false ;
  min_dl_font = 0 ;
  max_dl_font = 0 ; {printers down-loadable fonts}
@y
  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}
@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[ 8] := '1' ; {bugfix, 19-dec-1990, previously 6}
  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) ;
  cur_pge_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...@>=
  set_v_abs(0) ;
  set_h_abs(0) ;

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

@* Downloading, 1: reading the font file.

The simplest and crudest way this could possibly be done is: read the raster
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
raster file into an array. Then download each character before trying to print
it. This `lazy downloading' makes the program run much faster, at the price of
a large use of memory.
 @^\.{TUG}boat@>

@<Download a whole font@>=
  begin
    @<Prepare to read the |raster_file|@>
    repeat
      @<Get \.{GF} command |GF_com|, and do it@>
    until GF_com = 248;
    close_binary( raster_file) ;
    @<Establish the new font in memory@>
  end

@ First we have to determine the file name.

@<Prepare to read...@>=
  raster_mag := round(300 * 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;

@ 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 read the file; a horrible mess.

@<Get \.{GF} command...@>=
  GF_com := get_byte( raster) ;
  if GF_com <= 63 then paint( GF_com)
  else if (  GF_com >= 74) and (  GF_com <= 238) then
  new_row(  GF_com - 74 )
  else

@ Then we have the usual messy |case| statement:

@d three_cases(#)==
  #,#+1,#+2 : begin GF_par := get_integer( raster)( GF_com - # + 1 );
    four_case_end

@<Get \.{GF} command...@>=
  case GF_com of
    three_cases( 64)( paint( GF_par));
    67, 68: boc;
    69: eoc;
    70: miss_row( 0);
    three_cases( 71)( miss_row( GF_par));
    three_cases( 239)( skip( raster)( GF_par));
    242: skip( raster)( get_integer( raster)( -4)) ;
    243: skip( raster)( 4);
    244:;
    247: preamble;
    248: postamble ;
    245,246,249,250,251,252,253,254,255:
      warn( 'illegal GF command, will try to continue');
  end;
    @.illegal GF command@>

@ Now there follow lots of procedures to deal with the commands.

@<Lowest...@>=
  procedure preamble;
  var p: byte;
  begin
    p:=get_byte( raster);
    p:=get_byte( raster);
    skip( raster)( p) ;   {the introductory comment}
    with Font_box do begin
      L := pixel_R ;
      R := pixel_L ;
      T := pixel_B ;
      B := pixel_T ;
    end;
  end;

@#
  procedure postamble;
  var GF_check: integer ;
  begin
    skip( raster)( 8);
    GF_check := get_integer( raster)( -4) ;
    if (D_check<>0)and(GF_check<>0)and(D_check<>GF_check) then
    begin warn('check sums do not agree!');
        @.error: check sums...@>
      display_ln('DVI check was: ', D_check, ' GF check was: ', GF_check);
      display('   ');
    end;
  end;

@#
  procedure boc;
  var q : byte; mm, nn: i_word;
  begin
    if GF_com = 67 then
    begin GF_char := get_integer( raster)( 4) mod 256 ;
      skip( raster) ( 4);
      Boc_box.L:=get_integer( raster)( -4);
      Boc_box.R:=get_integer( raster)( -4);
      Boc_box.B:=get_integer( raster)( -4);
      Boc_box.T:=get_integer( raster)( -4);
    end
    else begin GF_char:=get_byte( raster);
      q:=get_byte( raster); Boc_box.R:=get_byte( raster);
      Boc_box.L:=Boc_box.R-q;
      q:=get_byte( raster); Boc_box.T:=get_byte( raster);
      Boc_box.B:=Boc_box.T-q;
    end;
    @<Clear the image@>
  end;

@ Now lets assign names to variables. In order to try to clear up the muddle
of boundaries for character cells, I introduce a concept of a ``box'', not the
same as a \TeX\ box. The fields represent the boundaries that must contain all
the black pixels of any character.

@<Const...@>=
  pixel_L = -300; pixel_R = 700; pixel_B = -300; pixel_T = 700;

@ @<Types...@>=
  @!x_coord=pixel_L..pixel_R;
  @!y_coord=pixel_B..pixel_T;
  box = packed record
    L,R: x_coord;
    T,B: y_coord;
  end;
  dir_entry = packed record
    point: integer;
    h_offset, v_offset, wid, ht : i_word;
  end;

@ @<Glob...@>=
  Boc_box,            {Limits of char. cell declared in |BOC| command}
  char_box,            {actual boundaries of char. cell}
  Font_box: box;       {The font cell.  This is the smallest cell that
      contains the reference point and any char. cell in the font}
  GF_com, GF_par, GF_char : byte;
  glyphs: packed array[1..max_glyph] of byte ;
  glyph_ptr: integer;
  raster_mag: integer;
  directory : array[D_font_ptr, D_char_ptr ] of dir_entry ;
  C_width : integer;

@ @<Set init...@>=
  glyph_ptr := 1 ;
  for in_i := 0 to max_D_fonts do
  for in_j := 0 to max_D_char do
  directory[ in_i, in_j].point := sentry ;
    {Mark everything as unprintable}

@ The definition of \.{GF} files refers to two registers, $(G_m,G_n)$, which
hold row and column numbers.  We also need to remember |paint_switch|, which
is either |black| or |white|.

@<Glob...@>=
  @!pixels: packed array [y_coord,x_coord] of pixel;
  @!G_m: x_coord;
  @!G_n: y_coord; {current state values}
  @!paint_switch: pixel;

@ We'll need a big array of pixels to hold the character image.  Each pixel
should be represented as a single bit in order to save space. Different
systems may prefer the following definitions, while others may do better using
the |boolean| type and constants.
 @^system dependencies@>

@d white==false
@d black==true
@d swop == paint_switch:=not paint_switch
{could also be |if paint_switch=black then paint_switch:=white
else paint_switch:=black|}

@<Types...@>=
@!pixel=boolean ;
 {could also be |white..black|}

@ Maybe there's a faster way to do this on your system.  Note that the only
part of the |image_array| that we clear is the part that the current character
may use.  Thus, the rest of this program may not look outside the area
delimited by |Boc_box| and expect to see anything but junk.
@^system dependencies@>

@<Clear the image@>=
  begin
    for nn := Boc_box.B to Boc_box.T do
    for mm := Boc_box.L to Boc_box.R do
    pixels[nn,mm] := white;
    G_n := Boc_box.T ;
    G_m := Boc_box.L ;
    char_box.L := pixel_R ;
    char_box.R := pixel_L ;
    char_box.B := pixel_T ;
    char_box.T := pixel_B ;
    directory[ nf, GF_char].point := 0 ;
      {Indicates a blank character}
  end

@ @<Lowest...@>=
  procedure paint( p: integer);
  var m, k: integer;
  begin
    if G_m+p> pixel_R then
    warn('character extends too far to the right')
      @.character extends...@>
    else if paint_switch = white then
    else begin
      m := G_m + p -1 ;
      if char_box.T < G_n then char_box.T := G_n ;
      if char_box.B > G_n then char_box.B := G_n ;
      if char_box.L > G_m then char_box.L := G_m ;
      for k:= G_m to m do
      pixels[G_n, k] := black ;
      if char_box.R < m then char_box.R := m ;
    end;
    swop;
    G_m := G_m +p ;
  end;

@#
procedure new_row( p:integer);
  begin
    decr(G_n);
    G_m:=Boc_box.L + p;
    paint_switch:=black;
  end;

@# procedure miss_row( p: integer);
  begin
    G_n:=G_n - p ;
    G_m:= Boc_box.L;
    paint_switch:=white;
  end;

@* Downloading, 2: Transfer characters into memory.

After we have read the character, we must transfer it into memory. First,
stretch the Font box to include the current characters box.

@<Lowest...@>=
  procedure eoc;
  var x, y : integer;
  cur_byt: byte ;
  q: i_word ;
  begin
    if char_box.R >= char_box.L {If not, the character is unprintable}
    then begin
      if char_box.L < Font_box.L then  Font_box.L := char_box.L ;
      if char_box.B < Font_box.B then  Font_box.B := char_box.B ;
      if char_box.R > Font_box.R then  Font_box.R := char_box.R ;
      if char_box.T > Font_box.T then  Font_box.T := char_box.T ;
      @<Transfer the dimensions of the character@>
      @<Transfer the |pixels| into |glyphs|@>
    end;
  end;

@ In landscape mode, the character must be rotated. The |directory| will
contain the dimensions to be downloaded; these do depend on orientation.
|char_box| describes the logical character which does not depend on
orientation.

@<Transfer the dimensions...@>=
  with directory[nf, GF_char] do begin
    point := glyph_ptr;
    if land then begin
      h_offset := - char_box.T ;
      v_offset := char_box.R ;
      ht := char_box.R - char_box.L +1 ;
      wid := char_box.T - char_box.B +1 ;
    end else begin
      h_offset := char_box.L ;
        {\TeX\ and the HP measure this in opposite directions}
      v_offset := char_box.T ;
      wid := char_box.R - char_box.L +1 ;
      ht := char_box.T - char_box.B +1 ;
    end;
  end;

@ Likewise the |glyphs| array will contain the pixels to be downloaded; these
also depend on orientation. Looking at the logical character, |x| is the
horizontal coordinate and |y| the vertical. In portrait mode the pixels must
be sent starting at the top left corner and going left to right along the top
row. In Landscape mode you must start at the top right corner and go down the
right hand column. Each row or column must be padded to |8n| bits.

@<Transfer the |pixels|...@>=
  if land then begin
    for x := char_box.R downto char_box.L do begin
      cur_byt := 0 ;
      q := 0 ;
      for y := char_box.T downto char_box.B do begin
        if pixels[ y, x] then cur_byt := cur_byt + powers[q] ;
        if q < 7 then incr( q)
        else begin
          glyphs[glyph_ptr] := cur_byt ; incr( glyph_ptr) ; cur_byt := 0;
          q := 0 ;
        end;
      end;
      if q > 0 then
      begin
        glyphs[glyph_ptr] := cur_byt ; incr( glyph_ptr) ; cur_byt := 0;
      end;
    end;
  end else

@ @<Transfer the |pixels|...@>=
  begin
    for y := char_box.T downto char_box.B do begin
      cur_byt := 0 ;
      q := 0 ;
      for x := char_box.L to char_box.R do begin
        if pixels[ y, x] then cur_byt := cur_byt + powers[q] ;
        if q < 7 then incr( q)
        else begin
          glyphs[glyph_ptr] := cur_byt ; incr( glyph_ptr) ; cur_byt := 0;
          q := 0 ;
        end;
      end;
      if q > 0 then
      begin
        glyphs[glyph_ptr] := cur_byt ; incr( glyph_ptr) ; cur_byt := 0;
      end;
    end;
  end ;

@ @<Set init...@>=
  powers[0] := 128 ;
  for in_i := 1 to 7 do powers[in_i] :=  powers[in_i-1] div 2 ;

@ @<Glob...@>= powers: array[0..7] of byte ;

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

@ @<Clean...@>=
  display_ln
    ('Used ', glyph_ptr:1, ' bytes of font memory out of ', max_glyph:1);

@ 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.

@<Establish...@>=
  incr(PR_dl_font ) ;
  if PR_dl_font > max_dl_font then
  font_error('tried to load too many fonts') ;
    @.Error: tried to load@>

@ @<Set init...@>=
  PR_dl_font := min_dl_font ;
  PR_mem_used := 0 ;
  PR_max_mem := 395000;
  be_string( '^[*c&DD' ) ;  font_start := buffer ;

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

@ 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. First we specify the font ID. This is a number by
which the printer will refer to the font after loading it.

@<Establish...@>=
  print_command( font_start, PR_dl_font, '^') ;
  print(chr(27), ')s26W' );
    {A create font command}
  prw(26);
  prw(1) ;  { 8 bit chars}
  prw(0) ;
  with Font_box do begin
    if L > 0 then L := 0 ;
    if B > 0 then B := 0 ;
    if R < 0 then R := 0 ;
    if T < 0 then T := 0 ; {Stretch the Font box to include the ref. point}
    prw( T) ;
    prw( R - L + 1) ; {width}
    prw( T - B + 1) ; {height}
  end;
  if land then prw( 257) else prw(1) ;  {proportional spaced}
  prw(277) ;
  for font_i := 1 to 5 do prw(0) ;
    {The HP needs these parameters, but they serve no purpose known to me}

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

@<Establish...@>=
  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 ;

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

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

@* Downloading, 3: 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 update the directory info. for the character, and assemble
its size parameters.

@<Enter a download...@>=
  with directory [ D_font, c_num] do begin
    if point < 0 then begin warn(
        'tried to print a non-existent character, number: ' , c_num:1) ;
      codes[ cur_scheme, c_num].breadth := bad_char ;
    end else begin
      C_length := ht * (( wid + 7) div 8) ; {length of data}
      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@>
        @.Error: tried to print...@>

@ @<Clean...@>=
  display_ln
  ('Used ', PR_mem_used:1, ' bytes of printers memory out of ',
    PR_max_mem:1) ;

@ @<Glob...@>=
  C_length, C_delta : integer;
  char_start, char_head: var_string ;

@ @<Set init...@>=
  be_string( '^[*c&DE' ) ;  char_start := buffer ;
  be_string( '^[(s&DW' ) ;  char_head := buffer ;

@ 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|.

@ 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...@>=
    { N.B. still |with directory [ D_font, c_num]| }
  print_command( font_start, cod.IM_font, '^') ;
    {Specify printers font identifier}
  print_command( char_start, cod.IM_char, '^') ;
  print_command( char_head, (C_length + 16), '^') ;
    {and the character}
  prw(1024);
  prw(14*256 + 1);
  if land then prw( 256 ) else prw(0);
  prw(h_offset) ;
  prw(v_offset);
  prw(wid) ;
  prw(ht) ;
  prw(4 * C_delta) ;

@ And at long last we can send the pixels!!

@<Enter a download...@>=
  for d_i := point to point+C_length-1 do
  print(chr(glyphs[ d_i] )) ;
  print_ln ;
end; 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