summaryrefslogtreecommitdiff
path: root/dviware/ln03/rmcs/dvitoln03.web
blob: 0690fcd579179fac7f9610bfbec895251b031e4f (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
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
6092
6093
6094
6095
6096
6097
6098
6099
6100
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140
6141
6142
6143
6144
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162
6163
6164
6165
6166
6167
6168
6169
6170
6171
6172
6173
6174
6175
6176
6177
6178
6179
6180
6181
6182
6183
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
6211
6212
6213
6214
6215
6216
6217
6218
6219
6220
6221
6222
6223
6224
6225
6226
6227
6228
6229
6230
6231
6232
6233
6234
6235
6236
6237
6238
6239
6240
6241
6242
6243
6244
6245
6246
6247
6248
6249
6250
6251
6252
6253
6254
6255
6256
6257
6258
6259
6260
6261
6262
6263
6264
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
6295
6296
6297
6298
6299
6300
6301
6302
6303
6304
6305
6306
6307
6308
6309
6310
6311
6312
6313
6314
6315
6316
6317
6318
6319
6320
6321
6322
6323
6324
6325
6326
6327
6328
6329
6330
6331
6332
6333
6334
6335
6336
6337
6338
6339
6340
6341
6342
6343
6344
6345
6346
6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368
6369
6370
6371
6372
6373
6374
6375
6376
6377
6378
6379
6380
6381
6382
6383
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
6439
6440
6441
6442
6443
6444
6445
6446
6447
6448
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
6479
6480
6481
6482
6483
6484
6485
6486
6487
6488
6489
6490
6491
6492
6493
6494
6495
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
6539
6540
6541
6542
6543
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
6565
6566
6567
6568
6569
6570
6571
6572
6573
6574
6575
6576
6577
6578
6579
6580
6581
6582
6583
6584
6585
6586
6587
6588
6589
6590
6591
6592
6593
6594
6595
6596
6597
6598
6599
6600
6601
6602
6603
6604
6605
6606
6607
6608
6609
6610
6611
6612
6613
6614
6615
6616
6617
6618
6619
6620
6621
6622
6623
6624
6625
6626
6627
6628
6629
6630
6631
6632
6633
6634
6635
6636
6637
6638
6639
6640
6641
6642
6643
6644
6645
6646
6647
6648
6649
6650
6651
6652
6653
6654
6655
6656
6657
6658
6659
6660
6661
6662
6663
6664
6665
6666
6667
6668
6669
6670
6671
6672
6673
6674
6675
6676
6677
6678
6679
6680
6681
6682
6683
6684
6685
6686
6687
6688
6689
6690
6691
6692
6693
6694
6695
6696
6697
6698
6699
6700
6701
6702
6703
6704
6705
6706
6707
6708
6709
6710
6711
6712
6713
6714
6715
6716
6717
6718
6719
6720
6721
6722
6723
6724
6725
6726
6727
6728
6729
6730
6731
6732
6733
6734
6735
6736
6737
6738
6739
6740
6741
6742
6743
6744
6745
6746
6747
6748
6749
6750
6751
6752
6753
6754
6755
6756
6757
6758
6759
6760
6761
6762
6763
6764
6765
6766
6767
6768
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
6779
6780
6781
6782
6783
6784
6785
6786
6787
6788
6789
6790
6791
6792
6793
6794
6795
6796
6797
6798
6799
6800
6801
6802
6803
6804
6805
6806
6807
6808
6809
6810
6811
6812
6813
6814
6815
6816
6817
6818
6819
6820
6821
6822
6823
6824
6825
6826
6827
6828
6829
6830
6831
6832
6833
6834
6835
6836
6837
6838
6839
6840
6841
6842
6843
6844
6845
6846
6847
6848
6849
6850
6851
6852
6853
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867
6868
6869
6870
6871
6872
6873
6874
6875
6876
6877
6878
6879
6880
6881
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891
6892
6893
6894
6895
6896
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
6912
6913
6914
6915
6916
6917
6918
6919
6920
6921
6922
6923
6924
6925
6926
6927
6928
6929
6930
6931
6932
6933
6934
6935
6936
6937
6938
6939
6940
6941
6942
6943
6944
6945
6946
6947
6948
6949
6950
6951
6952
6953
6954
6955
6956
6957
6958
6959
6960
6961
6962
6963
6964
6965
6966
6967
6968
6969
6970
6971
6972
6973
6974
6975
6976
6977
6978
6979
6980
6981
6982
6983
6984
6985
6986
6987
6988
6989
6990
6991
6992
6993
6994
6995
6996
6997
6998
6999
7000
7001
7002
7003
7004
7005
7006
7007
7008
7009
7010
7011
7012
7013
7014
7015
7016
7017
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
7034
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
7055
7056
7057
7058
7059
7060
7061
7062
7063
7064
7065
7066
7067
7068
7069
7070
7071
7072
7073
7074
7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
7090
7091
7092
7093
7094
7095
7096
7097
7098
7099
7100
7101
7102
7103
7104
7105
7106
7107
7108
7109
7110
7111
7112
7113
7114
7115
7116
7117
7118
7119
7120
7121
7122
7123
7124
7125
7126
7127
7128
7129
7130
7131
7132
7133
7134
7135
7136
7137
7138
7139
7140
7141
7142
7143
7144
7145
7146
7147
7148
7149
7150
7151
7152
7153
7154
7155
7156
7157
7158
7159
7160
7161
7162
7163
7164
7165
7166
7167
7168
7169
7170
7171
7172
7173
7174
7175
7176
7177
7178
7179
7180
7181
7182
7183
7184
7185
7186
7187
7188
7189
7190
7191
7192
7193
7194
7195
7196
7197
7198
7199
7200
7201
7202
7203
7204
7205
7206
7207
7208
7209
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
7225
7226
7227
7228
7229
7230
7231
7232
7233
7234
7235
7236
7237
7238
7239
7240
7241
7242
7243
7244
7245
7246
7247
7248
7249
7250
7251
7252
7253
7254
7255
7256
7257
7258
7259
7260
7261
7262
7263
7264
7265
7266
7267
7268
7269
7270
7271
7272
7273
7274
7275
7276
7277
7278
7279
7280
7281
7282
7283
7284
7285
7286
7287
7288
7289
7290
7291
7292
7293
7294
7295
7296
7297
7298
7299
7300
7301
7302
7303
7304
7305
7306
7307
7308
7309
7310
7311
7312
7313
7314
7315
7316
7317
7318
7319
7320
7321
7322
7323
7324
7325
7326
7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339
7340
7341
7342
7343
7344
7345
7346
7347
7348
7349
7350
7351
7352
7353
7354
7355
7356
7357
7358
7359
7360
7361
7362
7363
7364
7365
7366
7367
7368
7369
7370
7371
7372
7373
7374
7375
7376
7377
7378
7379
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423
7424
7425
7426
7427
7428
7429
7430
7431
7432
7433
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
7453
7454
7455
7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
7483
7484
7485
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
7498
7499
7500
7501
7502
7503
7504
7505
7506
7507
7508
7509
7510
7511
7512
7513
7514
7515
7516
7517
7518
7519
7520
7521
7522
7523
7524
7525
7526
7527
7528
7529
7530
7531
7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
7557
7558
7559
7560
7561
7562
7563
7564
7565
7566
7567
7568
7569
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581
7582
7583
7584
7585
7586
7587
7588
7589
7590
7591
7592
7593
7594
7595
7596
7597
7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
7619
7620
7621
7622
7623
7624
7625
7626
7627
7628
7629
7630
7631
7632
7633
7634
7635
7636
7637
7638
7639
7640
7641
7642
7643
7644
7645
7646
7647
7648
7649
7650
7651
7652
7653
7654
7655
7656
7657
7658
7659
7660
7661
7662
7663
7664
7665
7666
7667
7668
7669
7670
7671
7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
7720
7721
7722
7723
7724
7725
7726
7727
7728
7729
7730
7731
7732
7733
7734
7735
7736
7737
7738
7739
7740
7741
7742
7743
7744
7745
7746
7747
7748
7749
7750
7751
7752
7753
7754
7755
7756
7757
7758
7759
7760
7761
7762
7763
7764
7765
7766
7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
7782
7783
7784
7785
7786
7787
7788
7789
7790
7791
7792
7793
7794
7795
7796
7797
7798
7799
7800
7801
7802
7803
7804
7805
7806
7807
7808
7809
7810
7811
7812
7813
7814
7815
7816
7817
7818
7819
7820
7821
7822
7823
7824
7825
7826
7827
7828
7829
7830
7831
7832
7833
7834
7835
7836
7837
7838
7839
7840
7841
7842
7843
7844
7845
7846
7847
7848
7849
7850
7851
7852
7853
7854
7855
7856
7857
7858
7859
7860
7861
7862
7863
7864
7865
7866
7867
7868
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897
7898
7899
7900
7901
7902
7903
7904
7905
7906
7907
7908
7909
7910
7911
7912
7913
7914
7915
7916
7917
7918
7919
7920
7921
7922
7923
7924
7925
7926
7927
7928
7929
7930
7931
7932
7933
7934
7935
7936
7937
7938
7939
7940
7941
7942
7943
7944
7945
7946
7947
7948
7949
7950
7951
7952
7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
7974
7975
7976
7977
7978
7979
7980
7981
7982
7983
7984
7985
7986
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
8003
8004
8005
8006
8007
8008
8009
8010
8011
8012
8013
8014
8015
8016
8017
8018
8019
8020
8021
8022
8023
8024
8025
8026
8027
8028
8029
8030
8031
8032
8033
8034
8035
8036
8037
8038
8039
8040
8041
8042
8043
8044
8045
8046
8047
8048
8049
8050
8051
8052
8053
8054
8055
8056
8057
8058
8059
8060
8061
8062
8063
8064
8065
8066
8067
8068
8069
8070
8071
8072
8073
8074
8075
8076
8077
8078
8079
8080
8081
8082
8083
8084
8085
8086
8087
8088
8089
8090
8091
8092
8093
8094
8095
8096
8097
8098
8099
8100
8101
8102
8103
8104
8105
8106
8107
8108
8109
8110
8111
8112
8113
8114
8115
8116
8117
8118
8119
8120
8121
8122
8123
8124
8125
8126
8127
8128
8129
8130
8131
8132
8133
8134
8135
8136
8137
8138
8139
8140
8141
8142
8143
8144
8145
8146
8147
8148
8149
8150
8151
8152
8153
8154
8155
8156
8157
8158
8159
8160
8161
8162
8163
8164
8165
8166
8167
8168
8169
8170
8171
8172
8173
8174
8175
8176
8177
8178
8179
8180
8181
8182
8183
8184
8185
8186
8187
8188
8189
8190
8191
8192
8193
8194
8195
8196
8197
8198
8199
8200
8201
8202
8203
8204
8205
8206
8207
8208
8209
8210
8211
8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227
8228
8229
8230
8231
8232
8233
8234
8235
8236
8237
8238
8239
8240
8241
8242
8243
8244
8245
8246
8247
8248
8249
8250
8251
8252
8253
8254
8255
8256
8257
8258
8259
8260
8261
8262
8263
8264
8265
8266
8267
8268
8269
8270
8271
8272
8273
8274
8275
8276
8277
8278
8279
8280
8281
8282
8283
8284
8285
8286
8287
8288
8289
8290
8291
8292
8293
8294
8295
8296
8297
8298
8299
8300
8301
8302
8303
8304
8305
8306
8307
8308
8309
8310
8311
8312
8313
8314
8315
8316
8317
8318
8319
8320
8321
8322
8323
8324
8325
8326
8327
8328
8329
8330
8331
8332
8333
8334
8335
8336
8337
8338
8339
8340
8341
8342
8343
8344
8345
8346
8347
8348
8349
8350
8351
8352
8353
8354
8355
8356
8357
8358
8359
8360
8361
8362
8363
8364
8365
8366
8367
8368
8369
8370
8371
8372
8373
8374
8375
8376
8377
8378
8379
8380
8381
8382
8383
8384
8385
8386
8387
8388
8389
8390
8391
8392
8393
8394
8395
8396
8397
8398
8399
8400
8401
8402
8403
8404
8405
8406
8407
8408
8409
8410
8411
8412
8413
8414
8415
8416
8417
8418
8419
8420
8421
8422
8423
8424
8425
8426
8427
8428
8429
8430
8431
8432
8433
8434
8435
8436
8437
8438
8439
8440
8441
8442
8443
8444
8445
8446
8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
8462
8463
8464
8465
8466
8467
8468
8469
8470
8471
8472
8473
8474
8475
8476
8477
8478
8479
8480
8481
8482
8483
8484
8485
8486
8487
8488
8489
8490
8491
8492
8493
8494
8495
8496
8497
8498
8499
8500
8501
8502
8503
8504
8505
8506
8507
8508
8509
8510
8511
8512
8513
8514
8515
8516
8517
8518
8519
8520
8521
8522
8523
8524
8525
8526
8527
8528
8529
8530
8531
8532
8533
8534
8535
8536
8537
8538
8539
8540
8541
8542
8543
8544
8545
8546
8547
8548
8549
8550
8551
8552
8553
8554
8555
8556
8557
8558
8559
8560
8561
8562
8563
8564
8565
8566
8567
8568
8569
8570
8571
8572
8573
8574
8575
8576
8577
8578
8579
8580
8581
8582
8583
8584
8585
8586
8587
8588
8589
8590
8591
8592
8593
8594
8595
8596
8597
8598
8599
8600
8601
8602
8603
8604
8605
8606
8607
8608
8609
8610
8611
8612
8613
8614
8615
8616
8617
8618
8619
8620
8621
8622
8623
8624
8625
8626
8627
8628
8629
8630
8631
8632
8633
8634
8635
8636
8637
8638
8639
8640
8641
8642
8643
8644
8645
8646
8647
8648
8649
8650
8651
8652
8653
8654
8655
8656
8657
8658
8659
8660
8661
8662
8663
8664
8665
8666
8667
8668
8669
8670
8671
8672
8673
8674
8675
8676
8677
8678
8679
8680
8681
8682
8683
8684
8685
8686
8687
8688
8689
8690
8691
8692
8693
8694
8695
8696
8697
8698
8699
8700
8701
8702
8703
8704
8705
8706
8707
8708
8709
8710
8711
8712
8713
8714
8715
8716
8717
8718
8719
8720
8721
8722
8723
8724
8725
8726
8727
8728
8729
8730
8731
8732
8733
8734
8735
8736
8737
8738
8739
8740
8741
8742
8743
8744
8745
8746
8747
8748
8749
8750
8751
8752
8753
8754
8755
8756
8757
8758
8759
8760
8761
8762
8763
8764
8765
8766
8767
8768
8769
8770
8771
8772
8773
8774
8775
8776
8777
8778
8779
8780
8781
8782
8783
8784
8785
8786
8787
8788
8789
8790
8791
8792
8793
8794
8795
8796
8797
8798
8799
8800
8801
8802
8803
8804
8805
8806
8807
8808
8809
8810
8811
8812
8813
8814
8815
8816
8817
8818
8819
8820
8821
8822
8823
8824
8825
8826
8827
8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
8841
8842
8843
8844
8845
8846
8847
8848
8849
8850
8851
8852
8853
8854
8855
8856
8857
8858
8859
8860
8861
8862
8863
8864
8865
8866
8867
8868
8869
8870
8871
8872
8873
8874
8875
8876
8877
8878
8879
8880
8881
8882
8883
8884
8885
8886
8887
8888
8889
8890
8891
8892
8893
8894
8895
8896
8897
8898
8899
8900
8901
8902
8903
8904
8905
8906
8907
8908
8909
8910
8911
8912
8913
8914
8915
8916
8917
8918
8919
8920
8921
8922
8923
8924
8925
8926
8927
8928
8929
8930
8931
8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
8945
8946
8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
8975
8976
8977
8978
8979
8980
8981
8982
8983
8984
8985
8986
8987
8988
8989
8990
8991
8992
8993
8994
8995
8996
8997
8998
8999
9000
9001
9002
9003
9004
9005
9006
9007
9008
9009
9010
9011
9012
9013
9014
9015
9016
9017
9018
9019
9020
9021
9022
9023
9024
9025
9026
9027
9028
9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041
9042
9043
9044
9045
9046
9047
9048
9049
9050
9051
9052
9053
9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
9068
9069
9070
9071
9072
9073
9074
9075
9076
9077
9078
9079
9080
9081
9082
9083
9084
9085
9086
9087
9088
9089
9090
9091
9092
9093
9094
9095
9096
9097
9098
9099
9100
9101
9102
9103
9104
9105
9106
9107
9108
9109
9110
9111
9112
9113
9114
9115
9116
9117
9118
9119
9120
9121
9122
9123
9124
9125
9126
9127
9128
9129
9130
9131
9132
9133
9134
9135
9136
9137
9138
9139
9140
9141
9142
9143
9144
9145
9146
9147
9148
9149
9150
9151
9152
9153
9154
9155
9156
9157
9158
9159
9160
9161
9162
9163
9164
9165
9166
9167
9168
9169
9170
9171
9172
9173
9174
9175
9176
9177
9178
9179
9180
9181
9182
9183
9184
9185
9186
9187
9188
9189
9190
9191
9192
9193
9194
9195
9196
9197
9198
9199
9200
9201
9202
9203
9204
9205
9206
9207
9208
9209
9210
9211
9212
9213
9214
9215
9216
9217
9218
9219
9220
9221
9222
9223
9224
9225
9226
9227
9228
9229
9230
9231
9232
9233
9234
9235
9236
9237
9238
9239
9240
9241
9242
9243
9244
9245
9246
9247
9248
9249
9250
9251
9252
9253
9254
9255
9256
9257
9258
9259
9260
9261
9262
9263
9264
9265
9266
9267
9268
9269
9270
9271
9272
9273
9274
9275
9276
9277
9278
9279
9280
9281
9282
9283
9284
9285
9286
9287
9288
9289
9290
9291
9292
9293
9294
9295
9296
9297
9298
9299
9300
9301
9302
9303
9304
9305
9306
9307
9308
9309
9310
9311
9312
9313
9314
9315
9316
9317
9318
9319
9320
9321
9322
9323
9324
9325
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340
9341
9342
9343
9344
9345
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
9368
9369
9370
9371
9372
9373
9374
9375
9376
9377
9378
9379
9380
9381
9382
9383
9384
9385
9386
9387
9388
9389
9390
9391
9392
9393
9394
9395
9396
9397
9398
9399
9400
9401
9402
9403
9404
9405
9406
9407
9408
9409
9410
9411
9412
9413
9414
9415
9416
9417
9418
9419
9420
9421
9422
9423
9424
9425
9426
9427
9428
9429
9430
9431
9432
9433
9434
9435
9436
9437
9438
9439
9440
9441
9442
9443
9444
9445
9446
9447
9448
9449
9450
9451
9452
9453
9454
9455
9456
9457
9458
9459
9460
9461
9462
9463
9464
9465
9466
9467
9468
9469
9470
9471
9472
9473
9474
9475
9476
9477
9478
9479
9480
9481
9482
9483
9484
9485
9486
9487
9488
9489
9490
9491
9492
9493
9494
9495
9496
9497
9498
9499
9500
9501
9502
9503
9504
9505
9506
9507
9508
9509
9510
9511
9512
9513
9514
9515
9516
9517
9518
9519
9520
9521
9522
9523
9524
9525
9526
9527
9528
9529
9530
9531
9532
9533
9534
9535
9536
9537
9538
9539
9540
9541
9542
9543
9544
9545
9546
9547
9548
9549
9550
9551
9552
9553
9554
9555
9556
9557
9558
9559
9560
9561
9562
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576
9577
9578
9579
9580
9581
9582
9583
9584
9585
9586
9587
9588
9589
9590
9591
9592
9593
9594
9595
9596
9597
9598
9599
9600
9601
9602
9603
9604
9605
9606
9607
9608
9609
9610
9611
9612
9613
9614
9615
9616
9617
9618
9619
9620
9621
9622
9623
9624
9625
9626
9627
9628
9629
9630
9631
9632
9633
9634
9635
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646
9647
9648
9649
9650
9651
9652
9653
9654
9655
9656
9657
9658
9659
9660
9661
9662
9663
9664
9665
9666
9667
9668
9669
9670
9671
9672
9673
9674
9675
9676
9677
9678
9679
9680
9681
9682
9683
9684
9685
9686
9687
9688
9689
9690
9691
9692
9693
9694
9695
9696
9697
9698
9699
9700
9701
9702
9703
9704
9705
9706
9707
9708
9709
9710
9711
9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722
9723
9724
9725
9726
9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737
9738
9739
9740
9741
9742
9743
9744
9745
9746
9747
9748
9749
9750
9751
9752
9753
9754
9755
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770
9771
9772
9773
9774
9775
9776
9777
9778
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790
9791
9792
9793
9794
9795
9796
9797
9798
9799
9800
9801
9802
9803
9804
9805
9806
9807
9808
9809
9810
9811
9812
9813
9814
9815
9816
9817
9818
9819
9820
9821
9822
9823
9824
9825
9826
9827
9828
9829
9830
9831
9832
9833
9834
9835
9836
9837
9838
9839
9840
9841
9842
9843
9844
9845
9846
9847
9848
9849
9850
9851
9852
9853
9854
9855
9856
9857
9858
9859
9860
9861
9862
9863
9864
9865
9866
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887
9888
9889
9890
9891
9892
9893
9894
9895
9896
9897
9898
9899
9900
9901
9902
9903
9904
9905
9906
9907
9908
9909
9910
9911
9912
9913
9914
9915
9916
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928
9929
9930
9931
9932
9933
9934
9935
9936
9937
9938
9939
9940
9941
9942
9943
9944
9945
9946
9947
9948
9949
9950
9951
9952
9953
9954
9955
9956
9957
9958
9959
9960
9961
9962
9963
9964
9965
9966
9967
9968
9969
9970
9971
9972
9973
9974
9975
9976
9977
9978
9979
9980
9981
9982
9983
9984
9985
9986
9987
9988
9989
9990
9991
9992
9993
9994
9995
9996
9997
9998
9999
10000
10001
10002
10003
10004
10005
10006
10007
10008
10009
10010
10011
10012
10013
10014
10015
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
10031
10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046
10047
10048
10049
10050
10051
10052
10053
10054
10055
10056
10057
10058
10059
10060
10061
10062
10063
10064
10065
10066
10067
10068
10069
10070
10071
10072
10073
10074
10075
10076
10077
10078
10079
10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097
10098
10099
10100
10101
10102
10103
10104
10105
10106
10107
10108
10109
10110
10111
10112
10113
10114
10115
10116
10117
10118
10119
10120
10121
10122
10123
10124
10125
10126
10127
10128
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139
10140
10141
10142
10143
10144
10145
10146
10147
10148
10149
10150
10151
10152
10153
10154
10155
10156
10157
10158
10159
10160
10161
10162
10163
10164
10165
10166
10167
10168
10169
10170
10171
10172
10173
10174
10175
10176
10177
10178
10179
10180
10181
10182
10183
10184
10185
10186
10187
10188
10189
10190
10191
10192
10193
10194
10195
10196
10197
10198
10199
10200
10201
10202
10203
10204
10205
10206
10207
10208
10209
10210
10211
10212
10213
10214
10215
10216
10217
10218
10219
10220
10221
10222
10223
10224
10225
10226
10227
10228
10229
10230
10231
10232
10233
10234
10235
10236
10237
10238
10239
10240
10241
10242
10243
10244
10245
10246
10247
10248
10249
10250
10251
10252
10253
10254
10255
10256
10257
10258
10259
10260
10261
10262
10263
10264
10265
10266
10267
10268
10269
10270
10271
10272
10273
10274
10275
10276
10277
10278
10279
10280
10281
10282
10283
10284
10285
10286
10287
10288
10289
10290
10291
10292
10293
10294
10295
10296
10297
10298
10299
10300
10301
10302
10303
10304
10305
10306
10307
10308
10309
10310
10311
10312
10313
10314
10315
10316
10317
10318
10319
10320
10321
10322
10323
10324
10325
10326
10327
10328
10329
10330
10331
10332
10333
10334
10335
10336
10337
10338
10339
10340
10341
10342
10343
10344
10345
10346
10347
10348
10349
10350
10351
10352
10353
10354
10355
10356
10357
10358
10359
10360
10361
10362
10363
10364
10365
10366
10367
10368
10369
10370
10371
10372
10373
10374
10375
10376
10377
10378
10379
10380
10381
10382
10383
10384
10385
10386
10387
10388
10389
10390
10391
10392
10393
10394
10395
10396
10397
10398
10399
10400
10401
10402
10403
10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414
10415
10416
10417
10418
10419
10420
10421
10422
10423
10424
10425
10426
10427
10428
10429
10430
10431
10432
10433
% This program by D. E. Knuth is not copyrighted and can be used freely.
% Version 1 was completed in September, 1982.
% Slight changes were made in October, 1982, for version 0.7 of TeX.
% Version 1.1 corrected minor bugs (May, 1983).
% Version 2 was released with version 0.999 of TeX (July, 1983).
% Version 2.1 corrected a bug when no fonts are present (September, 1983).
% Version 2.2 corrected bugs in max_*_so_far and put1 (October, 1983).
% Version 2.3 corrected spacing of accents (March, 1984).
% Version 2.4 fixed rounding, changed oriental font conventions (April, 1984).
% Version 2.5 improved the case of zero pages (May, 1984).
% Version 2.6 introduced max_drift (June, 1984).
% Version 2.7 had minor editorial changes (August, 1984).

% Version 1.0 of DVItoLN03 was created (as a change file to DVItype) March 1986
%             by Brian HAMILTON KELLY, Royal Military College of Science,
%             Shrivenham, SWINDON, SN6 8LA, United Kingdom
%             Tel: Swindon (0793) 785252 (Int'l +44-793-785252)
%             e-mail: (Janet) tex@@uk.ac.cranfield.rmcs
%
%             This drew heavily upon Flavio Rose's DVI2LNG program, but also
%             included the work formerly performed as a separate pass by his
%             LN03TOPP program (which had bugs, and was written in PL/I, for
%             which we did not have a compiler!)
% Version 1.1 corrected minor bugs in the port from PL/I and displayed page
%             numbers as each was processed  April 1986
% Version 1.2 used .PXL files in separate directories for each magnification,
%             in the manner of Andrew Trevorrow's DVItoVDU  July 1986
% Version 1.3 introduced minor cosmetic changes and increased |max_rules| to
%             2048 July 1987
% Version 1.4 added support for including sixel graphics
%             through \special Sep 87
% Version 1.5 merged the original DVItype.WEB and the (now hopelessly huge)
%             change file, and `WEBified' the whole system.  Also added proper
%             support for default filenames, so that one CAN reference an
%             older DVI file than the latest version.
% Version 1.6 supports oversized glyphs (by making them into invisible
%             characters) and also outputs invisible characters as a single
%             pixel high row of white pixels of an appropriate width
% Version 1.7 prints the glyphs of oversized characters as bitmap sixel dumps.
% Version 2.0 reads PK files instead of .PXL (if available)
% Version 2.1 corrects error reading invisible fonts from packed files and
%             with invisible characters of zero TFM width.
% Version 3.0 includes wholesale rearrangement of some sections of code, to
%             improve its WEB-like quality.  It also adds the following
%             functionality:
%                 The packed and unpacked font files may be provided in
%                 either a flat or a rooted directory structure; if logical
%                 names are used to specify these locations (as in the .CLD
%                 file provided), the files may be spread over a number of
%                 different directories or volumes.
%                 The error messages are improved over earlier versions of
%                 the program, and are now all indexed in the woven
%                 (WEAVEd?) WEB.
%                 The program can now handle fonts with more then 128
%                 characters, up to TeX's limit of 256.  Therefore, it can
%                 now process documents which use Silvio Levy's Greek fonts.
%                 Retention of the log (.TYP) file may now be forced,
%                 suppressed, or allowed to be determined by the success of
%                 the processing.
%                 Minor revisions and corrections have been made, in
%                 particular, it now correctly understands the physical
%                 limitations to the imaging area.
% Version 3.1 correctly computes the magnification of each font for the font
%             loading reports, and also ensures that all output files are
%             opened in the current default directory, even when logical
%             names are used for the input file.  It also extracts and
%             reports the full file specification of the source (.DVI) file.
%             Also introduced the /OUTPUT qualifier and file spec on /LOG.
% Version 3.1-1 adds the `special' commands that were in the original Rose
%               program; this code was developed independently of (and
%               simultaneously with) version 3.1 (see above), and it therefore
%               seemed appropriate to give it a DEC-style version number
%               extension...
% Version 3.1-2 increases |max_rules| to 16384 to cope with the output of
%               BIGTeX.
% Version 3.1-3 reports an error status if the opening of a \special sixel
%               file fails.  The LN03 is also reset to pixel spacing units
%               after the sixel file has been included.
% Version 3.1-4 avoids attempting to print non-existent glyphs; some font
%               designers use non-existent characters in ligature sequences
%               (for example, Georgia Tobin ligatures * with characters such
%               as i, j, r, s, to select a variant form of the letter).
%               With earlier versions of this program, having failed to
%               download a glyph for the non-existent character, an attempt
%               would be made to print it is a bitmap when processing the
%               pages.
% Version 3.2   This version, and all others prior to V4.0, were never
%               released: they were purely internal to RMCS whilst
%               additional functionality was being incorporated.
%               This version tidied up Robin Fairbairns' code, particularly
%               for recognizing <dimen> units.
% Version 3.2-1 Correct code so that the arguments of \special are treated
%               as |ASCII_code| (they are not *necessarily* |char|s).
% Version 3.3   Provide qualifiers to define all logicals used, so that
%               sites may chose to use TEX$FONTS or TEX_FONTS, etc.
%               Permit use of units other than pixel for specification
%               of /LEFT_MARGIN and /TOP_MARGIN.
% Version 3.3-1 Permit /LEFT_MARGIN and /TOP_MARGIN to take dimensionless
%               quantity, with the assumption that it's in pixels.
% Version 3.4   Look out for virtual fonts, and reject them
% Version 3.4-1 Read VF file, and parse it
% Version 3.4-2 Map virtual font characters to real fonts, and load them
% Version 3.4-3 Now able to print simple characters from a VF
% Version 3.5   Made do_page capable of recursion to support complex
%               sequences in VF files
% Version 3.5-1 Revised DVI reading code for input from |vf|
% Version 3.5-2 Scaled |dvi| movements taken from VF file
% Version 3.5-3 Corrected computation of scaling of -ve movements
% Version 3.5-4 Removed confusion caused by empty segments on |seg_list|
% Version 3.5-5 Use font_scaled_size[vfno] for scaling physical fonts!
% Version 3.5-6 Corrected storage of -ve parameters in |vf| array
% Version 3.5-7 Terminate normal text segment preceding composite character
% Version 3.6   Read TFM files only once; fold upper/lower case names
% Version 3.6-1 Guard against infinite recursion with font recurse
% Version 3.6-2 Reference to another virtual font can't map to LN03 font
% Version 3.6-3 Correct spurious checksum error
% Version 3.7   Supports landscape and portrait \specials
% Version 3.7-1 Defers bitmap output of glyphs until |eop|, like rest
% Version 3.7-2 Correct handling of bitmap glyphs in |out_list|
% Version 3.8   Support mixture of orientations on same page
% Version 3.8-1 Moved all |max...| definitions to near front of WEB
% Version 3.8-2 Changed |varying [name_size] of char| into |file_spec|
% Version 3.8-3 Included narrative describing TFM files
% Version 3.8-4 Aggregate all |glyph_info| into one vector |glyphs|
% Version 3.8-5 Cosmetic changes; errors/warnings not just to TYP file
% Version 3.8-6 Preliminary moves to set rules for missing fonts
% Version 3.9   Image glyph from missing font by a solid rule
% Version 3.9-1 Integrate |pixel_width|, etc into |glyph_map|
% Version 3.9-2 Integrate |width|, etc into |glyph_map|
% Version 3.9-3 Further speed-up, by greater use of |with glyph_map|
% Version 3.9-4 Speed up setting loop, by removing double indexing
% Version 3.9-5 Defer text too wide message until eop; report phases
% Version 3.9-6 Couple more little speed-ups, missed before
% Version 3.9-7 Cosmetic amendments; improved indexing
% Version 3.9-8 Move <Move to top of form> before Copy_sixel_file
% Version 3.91  Start unravelling |print_mode| and trays at top of form
% Version 3.91-1 Acknowledge efforts of Robin and Karsten
% Version 3.91-2 Ensure |last_page_recto| set even first time through
% Version 3.91-3 Use Karsten's |blank_follows| mechanism
% Version 3.91-4 Trim trailing spaces from |cur_name| in error message
% Version 3.91-5 Determine |ln03_master| from /DEVICE_TYPE setting
% Version 3.91-6 Provide |rd_scaled_pt|, for decoding /hfuzz, /vfuzz
% Version 3.91-7 In duplex, don't change tray until 3rd page
% Version 3.91-8 Insert sixel dumps into |seg_list|
% Version 4.0    Cosmetic changes to error messages - new release!!!

\outer\def\N#1.#2.{\MN#1.\vfil\eject % beginning of starred section
  \def\rhead{\uppercase{\ignorespaces#2}} % define running headline
  \message{*\modno} % progress report
  {\def\.{\string\.}\def\BS{\string\BS\space}% Prevent expansion during contents write
    \edef\next{\write\cont{\Z{#2}{\modno}{\the\pageno}}}\next % to contents file
  }%
  \ifon\startsection{\bf\ignorespaces#2.\quad}\ignorespaces}


% Here is TeX material that gets inserted after \input webmac
\def\hang{\hangindent 3em\indent\ignorespaces}
\font\ninerm=cmr9
\let\mc=\ninerm % medium caps for names like PASCAL
\font\logo=logo10 % font used for the METAFONT logo
\def\MF{{\logo META}\-{\logo FONT}}
\def\PASCAL{P{\mc ASCAL}}

\def\(#1){} % this is used to make section names sort themselves better
\def\9#1{} % this is used for sort keys in the index

\def\title{DVI$\,$\lowercase{to}$\,$LN03 V4.0}
\def\contentspagenumber{0}
\def\topofcontents{\null
  \def\titlepage{F} % include headline on the contents page
  \def\rheader{\mainfont\hfil \contentspagenumber}
  \vfill
  \centerline{\titlefont The {\ttitlefont DVItoLN03} processor}
  \vskip 15pt
  \centerline{(Version 4.0, 27th February 1991)}
  \centerline{(Vax/VMS Version)}
  \vfill}
\def\botofcontents{\vfill
  \centerline{\mc Acknowledgements}
  \centerline{\hsize 5in\baselineskip9pt
    \vbox{\ninerm\noindent
    This program was originally based upon Flavio Rose's change file
    which, applied to David Fuch's original \.{DVItype} program, generated
    his \.{DVI2LNG} program.  (The output produced by \.{DVI2LNG} was then
    further processed by \.{LN03TOPP} to yield a \.{LN3} file to drive a
    Digital LN03 laser printer.)  Further extensions and enhancements were
    added, and when the \.{WEB} change file grew to twice the size of the
    original \.{DVITYPE.WEB} source, a manual \.{WEB}merge operation was
    performed!\endgraf
    Support for \.{\\changebar} specials was provided by Robin Fairbairns.
    The code for the newer DEClaser printers is due to Karsten Nyblad.}}}
\pageno=\contentspagenumber \advance\pageno by 1
\let\maybe=\iftrue
%\let\maybe=\iffalse
\def\meta#1{{$\langle$\rm #1$\rangle$}}
\def\9#1{}% Used with @@:sort entry}{printed entry@@>

@* Introduction.
The \.{DVItoLN03} utility program reads binary device-independent (``\.{DVI}'')
files that are produced by document compilers such as \TeX, and converts them
into a form suited to driving a Digital LN03 or LN03-Plus laser printer.  The
@^LN03 laser printer@>
output file (with extension \.{LN3}) contains both the instructions to typeset
@^LN3 file@>
the required characters \&{and} the commands necessary to download the
characters required (until DEC get around to providing the Computer Modern
fonts in a ROM cartridge!).  The LN03-Plus will accept, without complaint,
any number of downloaded fonts: however, only the first 14 fonts (each
consisting of up to 188 characters, in the \\{GL} and \\{GR} sets) are
actually usable for imaging glyphs: attempts to image a glyph from fonts
|15..| result in the display of a character taken from one of the first 14
fonts loaded.  In view of these limitations, \.{DVItoLN03} only downloads
the characters which are actually used within the document being processed;
the maximum possible number of characters, even if taken from different
\TeX\ fonts, are combined into each single `LN03 font'.

The program was developed by Brian Hamilton Kelly at the Royal Military
@^Hamilton Kelly, Brian@>
College of Science.
@^RMCS@>
@^Royal Military College of Science@>
The \.{DVI2LNG} program of Flavio Rose
@^Rose, Flavio@>
@:DVI2LNG program}{\.{DVI2LNG} program@>
was used as a starting point; this had been distributed as a change file to
be applied to the original \.{DVItype} program,
@:DVItype Program}{\.{DVItype} program@>
and ``translated'' the \.{DVI} file into two output files which were then
further processed by his \.{LN03toPP}
@:LN03toPP Program}{\.{LN03toPP} program@>
program (written in PL/I), which required device-specific versions of
unpacked pixel files.  The initial step was to perform the two separate
passes within a single program (therefore the present program still needs to
make two passes through the \.{DVI} file).  Later work added further
functionality including use of ``ordinary'' pixel files  and support for
\TeX's \.{\BS special} command to read sixel graphics dump files and include
them verbatim within the output file.  More recent revisions have produced
major versions 2, 3 and 4:

\yskip\hang\&{V2.0} Provided capability to read pixel rasters from packed
(\.{PK}) files as well as from the traditional \.{PXL} files.  It also manages
to handle the glyphs of  characters which are too large to load into the
LN03's memory.

\yskip\hang\&{V3.0} Changed method of accessing font files, to allow users
flexibility in the storage philosophy they adopt.  Can now handle large
(256-character) fonts, such as Levy's Greek.
@^Levy, Silvio@>
@^256-character fonts@>
@:two hundred and fifty six character fonts}{256-character fonts@>


\yskip\hang\&{V3.1} Robin Fairbairns of Laser Scan (Cambridge, UK) added
@^Fairbairns, Robin@>
support for Rose's \.{\BS special}s for changebar marking at V3.1-1, June
1989.

\yskip\hang\&{V3.2} Tidied up recognition of \TeX\ \meta{physical unit}s.
Removed final vestige of `hard-wired' logical names, \\{viz:} \.{TEX\$FONTS}
@.TEX\$FONTS@>

\yskip\hang\&{V3.3} Permitted \.{/LEFT\UL MARGIN} and \.{TOP\UL MARGIN}
qualifiers to take dimensioned lengths, instead of being restricted to
pixels.

\yskip\hang\&{V3.4} Processed virtual font files, provided each character
was only mapped to a single character from some other font; did not support
virtual characters which mapped to a sequence of one or more characters from
other physical fonts, nor could it handle font \.{recurse}.

\yskip\hang\&{V3.5} Handled sequences in virtual font files; still couldn't
handle font \.{recurse}.

\yskip\hang\&{V3.6} Supports font \.{recurse}; folds multiple references to
same font (name and scale) onto single invocation.

\yskip\hang\&{V3.7} Supports landscape and portrait specials; whole page only.

\yskip\hang\&{V3.8} Supports mixture of landscape and portrait on same page.

\yskip\hang\&{V3.9} Prints glyphs from missing fonts as solid rules; added
\.{/HFUZZ} and \.{/VFUZZ} qualifiers.

\yskip\hang\&{V4.0} Supports the new DEClaser~2100 and~2200 printers (the
LN05 and LN06) in addition to the traditional LN03.  With the LN06, paper
feed trays may be selected from the command line, and the duplexing
facilities controlled also.  Support for these printers was provided by
Karsten Nyblad
@^Nyblad, Karsten@>
of TFL, the Danish Telecommunication Research Laboratory, and integrated
into this new release with the other features mentioned above.

One of the stated {\it raisons d'\^^Detre\/} for \.{DVItype} is ``to serve as an
example of a program that reads \.{DVI} files correctly, for system
programmers who are developing \.{DVI}-related software.''   The paradigms for
calculation provided by this example have been followed closely in the present
program (by importing them {\it verbatim\/} from \.{DVItype}!)  To quote again
from \.{DVItype}:\vskip 5pt

  \centerline{\hsize 5in\baselineskip9pt
    \vbox{\ninerm\noindent
      ``Programs for typesetting need to be especially careful about how they do
      arithmetic; if rounding errors accumulate, margins won't be straight,
      vertical rules won't line up, and so on. But if rounding is done
      everywhere, even in the midst of words, there will be uneven spacing
      between the letters, and that looks bad. Human eyes notice differences
      of a thousandth of an inch in the positioning of lines that are close
      together; on low resolution devices, where rounding produces effects
      four times as great as this, the problem is especially critical.
      Experience has shown that unusual care is needed even on high-resolution
      equipment; for example, a mistake in the sixth significant hexadecimal
      place of a constant once led to a difficult-to-find bug in some software
      for the Alphatype CRS, which has a resolution of 5333 pixels per inch
      (make that 5333.33333333 pixels per inch).  The document compilers that
      generate \.{DVI} files make certain assumptions about the arithmetic
      that will be used by \.{DVI}-reading software, and if these assumptions
      are violated the results will be of inferior quality.''
   }}

The first \.{DVItype} program was designed by David Fuchs in 1979, and it
@^Fuchs, David Raymond@>
went through several versions on different computers as the format of
\.{DVI} files was evolving to its present form.

The \.{LN3} output file produced by DVItoLN03 always commences by including
commands to down-load one or more font-files (each of a maximum of 188
characters) to define just those characters used in the document. The rest of
the file consists of commands to set those characters where required.  Because
the mapping of source characters to those in the down-loaded fonts is
arbitrary, the output file is unintelligible to human readers!

Whilst many of the earlier changes tried to preserve the
``machine-independence'' of the program, no attempt has been made in this
version, since it is assumed that the LN03 printer will be used on a VAX
computer.  In particular, |random_reading| is used on some of the input files,
and is necessary.

The |banner| string defined here should be changed whenever \.{DVItoLN03}
gets modified.

@d banner=='This is DVItoLN03, Vax/VMS Version 4.0'

@ This program is written mostly in standard \PASCAL, except where it is
necessary to use extensions; for example, \.{DVItoLN03} must read files whose
names are dynamically specified, and that would be impossible in pure \PASCAL.
Many features of VAX-\PASCAL\ have been incorporated, and no attempt has been
made to ensure that all places where such nonstandard constructions are used
have been listed in the index under ``system dependencies.''
@!@^system dependencies@>

One of the extensions to standard \PASCAL\ that we shall deal with is the
ability to move to a random place in a binary file; another is to
determine the length of a binary file. Such extensions are not necessary
for reading \.{DVI} files, and they are not important for efficiency
reasons either.  However, when creating the `font files' for
downloading the necessary characters to the LN03, \.{DVItoLN03} \&{has} to be
able to perform |random_reading|, to locate the glyph of each character within
the pixel file.  Therefore, if \.{DVItoLN03} is being used with
\PASCAL s for which random file positioning is not efficiently available,
and the following definition is changed from |true| to |false|, further work
will still be required to re-code the processing of the pixel files.

Another extension is to use a default |case| as in \.{TANGLE}, \.{WEAVE},
etc.


@d random_reading==true {should we skip around in the file?}
@d othercases == otherwise {Vax/VMS default for cases not listed explicitly}
@d endcases == @+end {follows the default case in an extended |case| statement}
@f othercases == else
@f endcases == end

@ The binary input comes from |dvi_file|, and the \.{LN3} file is written to
|ln3_file|.  \.{DVItoLN03} also produces a `log' file using |type_file|.
Errors and some informational messages are written to this, and are also
written to \PASCAL's standard |output| file.  The term |print| is used instead
of |write| when this program writes on |type_file|, so that all such output
could easily be redirected if desired.

Under Vax/VMS we use double-precision for all real values.  Furthermore, since
extensive reference is made to Vax/VMS system services and other library
routines, we arrange for the \PASCAL\ program to `inherit' all their
definitions (from \.{STARLET}).

@d print(#)==write(type_file,#)
@d print_ln(#)==write_ln(type_file,#)
@d real==double

@p @=[INHERIT ('SYS$LIBRARY:STARLET')]@> program DVI_to_LN03(@!dvi_file,
                                @!tfm_file,@!type_file,@!output,@!ln3_file);
label @<Labels in the outer block@>@/
const @<Constants in the outer block@>@/
type @<Types in the outer block@>@/
var @<Globals in the outer block@>@/
procedure jump_out; forward;@#
@t\2@>@<Procedures for initialization@>@;@#
procedure initialize; {this procedure gets things started properly}
  var @!i:integer; {loop index for initializations}
  begin
  @<Preset initial values@>@/
  print_ln(banner);@/
  @<Set initial values@>@/
  end;@#
@<Additional low-level procedures@>@;

@ If the program has to stop prematurely, it goes to the
`|final_end|'. Another label, |done|, is used when stopping normally.

@d final_end=9999 {label for the end of it all}
@d done=30 {go here when finished with a subtask}

@<Labels...@>=final_end,done;

@ The following parameters can be changed at compile time to extend or
reduce \.{DVItoLN03}'s capacity.

As previously mentioned, the LN03-plus is quite happy to accept any number
of downloaded fonts, but is incapable of imaging glyphs from more than
fourteen of them!  (The basic LN03 won't accept more than 21 half-fonts.)
Each such font has to be selected by mapping it to one of 10 different
Select Graphic Renditions; this is reflected by the constant |max_SGR|.

Whenever bitmaps are sent to the printer (in sixel format), it is necessary
to establish the ``printing'' position 29 pixels  above the current
reference point. Despite repeated enquiry of DEC, it has not been
established why this is necessary. Naturally, we call this 29 pixels
|bitmap_offset|.  This same offset also has to be applied to sixel graphics
included through a \.{\\special} command.

When the paper orientation is selected, we may define the |page_len| and
|page_wid| in terms of the physical limits of the paper; these sizes are
as follows (for European A4 paper).
@^European A4 paper@>
(These might later become variables, and be alterable by qualifiers in the
command line.)  There is, however, a further complication.  In either printing
orientation, the printable area is of these dimensions; however, this printing
area starts 0.25~inches from the top left corner of the paper \\{in the
orientation it enters the printer}.  Therefore in portrait mode, we can
actually print from (75,75) to (2475,3475), with (300,300) being the
``normal'' \TeX\ origin, but in landscape mode we can print (35,75) to
(3435,2475), and \TeX's origin should still be at (300,300).

The LN03 has a physical (apparently arbitrary) restriction on the maximum size
of a downloadable glyph.  For a glyph to be acceptable, it must satisfy the
inequality $$
  2\times\lceil|num_cols|/2\rceil\times\lceil|num_rows|/8\rceil\le5700$$

The type |file_spec| is used for declaring data objects containing VAX/RMS
file specifications, and for declaring procedures which handle them.

@d max_line=200         {Maximum length of any line output to the LN03}
@d max_print=175        {Maximum number of characters imaged on line}
@#
@d max_lnfonts=13       {Limitations of the LN03's hardware}
@d max_SGR=9
@#
@d max_special=300 {maximum size of a \.{\\special} command that we can deal
with}
@d max_points=256 {number of points for the \.{\\special} rule commands}
@d max_strings=30       {Distinct string parameters of \.{\\special}s}
@d pool_size=100        {Total characters in those strings}
@#
@d bitmap_offset=29
@#
@d paper_ht=3400                {in pixels}
@d paper_wid=2400
@d y_port_min=75
@d y_land_min=35
@d page_x_min=75
@d y_top=75       {Minimum addressable $y$ pixel}
@d y_bot=paper_ht+y_port_min {Maximum addressable $y$ pixel}
@#
@d PFS_landscape=='?23 J' {Extended A4 paper in landscape orientation}
@d PFS_portrait=='?22 J'  {ditto in portrait; each preceded by |csi|}
@#
@d lnf_bufsize=2000     {Maximum block size of an LN03 ``font file'' in memory ($\approx1$MB)}
@#
@d largest_glyph=5700
@#
@d max_fonts=100 {maximum number of distinct fonts per \.{DVI} file}
@d max_glyphs=10000 {maximum number of different characters among all fonts}
@#
@d line_length=79 {bracketed lines of output will be at most this long}
@d terminal_line_length=150 {maximum number of characters input in a single
  line of input from the command line interpreter}
@#
@d stack_size=100 {\.{DVI} files shouldn't |push| beyond this depth}
@d name_size=1000 {total length of all font file names}
@#
@d name_length=255 {a file name shouldn't be longer than this}
@#
@d max_rules=16384 {maximum number of rules per page}
@d max_lap_rules=3 {maximum under/over lap of rules}
@#
@d vf_size=10000 {maximum |dvi| commands stored from virtual font files}
@#
@d max_blank_pages=100 {blank \\{verso} pages between successive \\{recto}s}

@<Types...@>=
@!file_spec = varying [name_length] of char;

@ Here are some macros for common programming idioms.

@d incr(#) == #:=#+1 {increase a variable by unity}
@d decr(#) == #:=#-1 {decrease a variable by unity}
@d do_nothing == {empty statement}

@ If the \.{DVI} file is badly malformed, the whole process must be aborted;
\.{DVItoLN03} will give up, after issuing an error message about the symptoms
that were noticed.

Such errors might be discovered inside of subroutines inside of subroutines,
so a procedure called |jump_out| has been introduced. This procedure, which
simply transfers control to the label |final_end| at the end of the program,
contains the only non-local |goto| statement in \.{DVItoLN03}.
@^system dependencies@>

@d crlf==chr(13)+chr(10)
@d abort(#)==begin
        history := fatal_error; print_ln(' ');
        print_ln(' Fatal error: ',#);
@!@.Fatal error ...@>
        write_ln(output,crlf,'Fatal error: ',#,crlf);
        jump_out;
    end
@d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
@!@.Bad DVI file@>
@!@:fatal error Bad DVI file}{\quad\.{Bad DVI file} \\{(q.v.)}@>
@d capacity_exceeded(#)==abort('capacity exceeded [',#,']')
@!@.capacity exceeded@>
@!@:fatal error capacity exceeded}{\quad\.{capacity exceeded} \\{(q.v.)}@>

@p procedure jump_out;
begin goto final_end;
end;

@* The character set.
Like all programs written with the  \.{WEB} system, \.{DVItoLN03} can be
used with any character set. But it uses ASCII code internally, because
the programming for portable input-output is easier when a fixed internal
code is used, and because \.{DVI} files use ASCII code for file names
and certain other strings.

The next few sections of \.{DVItoLN03} have therefore been copied from the
analogous ones in the \.{WEB} system routines. They have been considerably
simplified, since \.{DVItoLN03} need not deal with the controversial
ASCII codes less than @'40. If such codes appear in the \.{DVI} file,
they will be printed as question marks.

All characters still occupy 8 bits of storage, to avoid overhead with |packed
array|s.

@<Types...@>=
@!ASCII_code= @=[byte]@> " ".."~"; {a subrange of the integers}

@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
character sets were common, so it did not make provision for lower case
letters. Nowadays, of course, we need to deal with both upper and lower case
alphabets in a convenient way, especially in a program like \.{DVItoLN03}.
So we shall assume that the \PASCAL\ system being used for \.{DVItoLN03}
has a character set containing at least the standard visible characters
of ASCII code (|"!"| through |"~"|).

Some \PASCAL\ compilers use the original name |char| for the data type
associated with the characters in text files, while other \PASCAL s
consider |char| to be a 64-element subrange of a larger data type that has
some other name.  In order to accommodate this difference, we shall use
the name |text_char| to stand for the data type of the characters in the
output file.  We shall also assume that |text_char| consists of
the elements |chr(first_text_char)| through |chr(last_text_char)|,
inclusive. The following definitions should be adjusted if necessary.
@^system dependencies@>

@d text_char == char {the data type of characters in text files}
@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
@d last_text_char=255 {ordinal number of the largest element of |text_char|}

@<Types...@>=
@!text_file=packed file of text_char;

@ The \.{DVItoLN03} processor converts between ASCII code and
the user's external character set by means of arrays |xord| and |xchr|
that are analogous to \PASCAL's |ord| and |chr| functions.

@<Globals...@>=
@!xord: array [text_char] of ASCII_code;
  {specifies conversion of input characters}
@!xchr: array [first_text_char..last_text_char] of text_char;
  {specifies conversion of output characters}

@ Under our assumption that the visible characters of standard ASCII are
all present, the following assignment statements initialize the
|xchr| array properly, without needing any system-dependent changes.

@<Set init...@>=
for i:=0 to @'37 do xchr[i]:='?';
xchr[@'40]:=' ';
xchr[@'41]:='!';
xchr[@'42]:='"';
xchr[@'43]:='#';
xchr[@'44]:='$';
xchr[@'45]:='%';
xchr[@'46]:='&';
xchr[@'47]:='''';@/
xchr[@'50]:='(';
xchr[@'51]:=')';
xchr[@'52]:='*';
xchr[@'53]:='+';
xchr[@'54]:=',';
xchr[@'55]:='-';
xchr[@'56]:='.';
xchr[@'57]:='/';@/
xchr[@'60]:='0';
xchr[@'61]:='1';
xchr[@'62]:='2';
xchr[@'63]:='3';
xchr[@'64]:='4';
xchr[@'65]:='5';
xchr[@'66]:='6';
xchr[@'67]:='7';@/
xchr[@'70]:='8';
xchr[@'71]:='9';
xchr[@'72]:=':';
xchr[@'73]:=';';
xchr[@'74]:='<';
xchr[@'75]:='=';
xchr[@'76]:='>';
xchr[@'77]:='?';@/
xchr[@'100]:='@@';
xchr[@'101]:='A';
xchr[@'102]:='B';
xchr[@'103]:='C';
xchr[@'104]:='D';
xchr[@'105]:='E';
xchr[@'106]:='F';
xchr[@'107]:='G';@/
xchr[@'110]:='H';
xchr[@'111]:='I';
xchr[@'112]:='J';
xchr[@'113]:='K';
xchr[@'114]:='L';
xchr[@'115]:='M';
xchr[@'116]:='N';
xchr[@'117]:='O';@/
xchr[@'120]:='P';
xchr[@'121]:='Q';
xchr[@'122]:='R';
xchr[@'123]:='S';
xchr[@'124]:='T';
xchr[@'125]:='U';
xchr[@'126]:='V';
xchr[@'127]:='W';@/
xchr[@'130]:='X';
xchr[@'131]:='Y';
xchr[@'132]:='Z';
xchr[@'133]:='[';
xchr[@'134]:='\';
xchr[@'135]:=']';
xchr[@'136]:='^';
xchr[@'137]:='_';@/
xchr[@'140]:='`';
xchr[@'141]:='a';
xchr[@'142]:='b';
xchr[@'143]:='c';
xchr[@'144]:='d';
xchr[@'145]:='e';
xchr[@'146]:='f';
xchr[@'147]:='g';@/
xchr[@'150]:='h';
xchr[@'151]:='i';
xchr[@'152]:='j';
xchr[@'153]:='k';
xchr[@'154]:='l';
xchr[@'155]:='m';
xchr[@'156]:='n';
xchr[@'157]:='o';@/
xchr[@'160]:='p';
xchr[@'161]:='q';
xchr[@'162]:='r';
xchr[@'163]:='s';
xchr[@'164]:='t';
xchr[@'165]:='u';
xchr[@'166]:='v';
xchr[@'167]:='w';@/
xchr[@'170]:='x';
xchr[@'171]:='y';
xchr[@'172]:='z';
xchr[@'173]:='{';
xchr[@'174]:='|';
xchr[@'175]:='}';
xchr[@'176]:='~';
for i:=@'177 to 255 do xchr[i]:='?';

@ The following system-independent code makes the |xord| array contain a
suitable inverse to the information in |xchr|.

@<Set init...@>=
for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
for i:=" " to "~" do xord[xchr[i]]:=i;

@* Device-independent file format.
Before we get into the details of \.{DVItoLN03}, we need to know exactly
what \.{DVI} files are. The form of such files was designed by David R.
@^Fuchs, David Raymond@>
Fuchs in 1979. Almost any reasonable typesetting device can be driven by
a program that takes \.{DVI} files as input, and dozens of such
\.{DVI}-to-whatever programs have been written. Thus, it is possible to
print the output of document compilers like \TeX\ on many different kinds
of equipment.

A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
series of commands in a machine-like language. The first byte of each command
is the operation code, and this code is followed by zero or more bytes
that provide parameters to the command. The parameters themselves may consist
of several consecutive bytes; for example, the `|set_rule|' command has two
parameters, each of which is four bytes long. Parameters are usually
regarded as nonnegative integers; but four-byte-long parameters,
and shorter parameters that denote distances, can be
either positive or negative. Such parameters are given in two's complement
notation. For example, a two-byte-long distance parameter has a value between
$-2^{15}$ and $2^{15}-1$.
@.DVI {\rm files}@>

A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
|pre| command, with its parameters that define the dimensions used in the
file; this must come first.  Each ``page'' consists of a |bop| command,
followed by any number of other commands that tell where characters are to
be placed on a physical page, followed by an |eop| command. The pages
appear in the order that they were generated, not in any particular
numerical order. If we ignore |nop| commands and \\{fnt\_def} commands
(which are allowed between any two commands in the file), each |eop|
command is immediately followed by a |bop| command, or by a |post|
command; in the latter case, there are no more pages in the file, and the
remaining bytes form the postamble.  Further details about the postamble
will be explained later.

Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
quantities that give the location number of some other byte in the file;
the first byte is number~0, then comes number~1, and so on. For example,
one of the parameters of a |bop| command points to the previous |bop|;
this makes it feasible to read the pages in backwards order, in case the
results are being directed to a device that stacks its output face up.
Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
first page occupies bytes 100 to 999, say, and if the second
page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
very first |bop|, i.e., the one that starts in byte 100, has a pointer of $-1$.)

@<Glob...@>=
@!dvi_file:byte_file; {the stuff we are \.{DVItoLN03}ing}

@ The \.{DVI} format is intended to be both compact and easily interpreted
by a machine. Compactness is achieved by making most of the information
implicit instead of explicit. When a \.{DVI}-reading program reads the
commands for a page, it keeps track of several quantities: (a)~The current
font |f| is an integer; this value is changed only
by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
is given by two numbers called the horizontal and vertical coordinates,
|h| and |v|. Both coordinates are zero at the upper left corner of the page;
moving to the right corresponds to increasing the horizontal coordinate, and
moving down corresponds to increasing the vertical coordinate. Thus, the
coordinates are essentially Cartesian, except that vertical directions are
flipped; the Cartesian version of |(h,v)| would be |(h,-v)|.  (c)~The
current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
are used for vertical spacing. (d)~There is a stack containing
|(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to
change the current level of operation. Note that the current font~|f| is
not pushed and popped; the stack contains only information about
positioning.

The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
to 32 bits, including the sign. Since they represent physical distances,
there is a small unit of measurement such that increasing |h| by~1 means
moving a certain tiny distance to the right. The actual unit of
measurement is variable, as explained below.

@ Here is a list of all the commands that may appear in a \.{DVI} file. Each
command is specified by its symbolic name (e.g., |bop|), its opcode byte
(e.g., 139), and its parameters (if any). The parameters are followed
by a bracketed number telling how many bytes they occupy; for example,
`|p[4]|' means that parameter |p| is four bytes long.

\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
such that the reference point of the character is at |(h,v)|. Then
increase |h| by the width of that character. Note that a character may
have zero or negative width, so one cannot be sure that |h| will advance
after this command; but |h| usually does increase.

\yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127).
Do the operations of |set_char_0|; but use the character whose number
matches the opcode, instead of character~0.

\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
number~|c| is typeset. \TeX82 uses this command for characters in the
range |128<=c<256|.

\yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this
command, which is intended for processors that deal with oriental languages;
but \.{DVItoLN03} will allow character codes greater than 255, assuming that
they all have the same width as the character whose code is $c \bmod 256$.
@^oriental characters@>@^Chinese characters@>@^Japanese characters@>

\yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
bytes long, so it can be as large as $2^{24}-1$.

\yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
bytes long, possibly even negative. Imagine that.

\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
of height |a| and width |b|, with its bottom left corner at |(h,v)|. Then
set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
that if |b<0|, the value of |h| will decrease even though nothing else happens.
Programs that typeset from \.{DVI} files should be careful to make the rules
line up carefully with digitized characters, as explained in connection with
the |rule_pixels| subroutine below.

\yskip\hang|put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
such that the reference point of the character is at |(h,v)|. (The `put'
commands are exactly like the `set' commands, except that they simply put out a
character or a rule without moving the reference point afterwards.)

\yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.

\yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.

\yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.

\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
|h| is not changed.

\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
may occur between \.{DVI} commands, but a |nop| cannot be inserted between
a command and its parameters or between two parameters.

\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
the current font |f| to an undefined value.  The ten $c_i$ parameters can
be used to identify pages, if a user wants to print only part of a \.{DVI}
file; \TeX82 gives them the values of \.{\\count0} $\ldots$ \.{\\count9}
at the time \.{\\shipout} was invoked for this page.  The parameter |p|
points to the previous |bop| command in the file, where the first |bop|
has $p=-1$.

\yskip\hang|eop| 140.  End of page: Print what you have read since the
previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
programs that drive most output devices will have kept a buffer of the
material that appears on the page that has just ended. This material is
largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
|h|~coordinate; so it usually needs to be sorted into some order that is
appropriate for the device in question. \.{DVItoLN03} does do such sorting.)

\yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the
top of the stack; do not change any of these values. Note that |f| is
not pushed.

\yskip\hang|pop| 142. Pop the top six values off of the stack and assign
them to |(h,v,w,x,y,z)|. The number of pops should never exceed the number
of pushes, since it would be highly embarrassing if the stack were empty
at the time of a |pop| command.

\yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
The parameter is a signed number in two's complement notation, |-128<=b<128|;
if |b<0|, the reference point actually moves left.

\yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a
two-byte quantity in the range |-32768<=b<32768|.

\yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a
three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.

\yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a
four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.

\yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
this parameterless command will usually suffice, because the same kind of motion
will occur several times in succession; the following commands explain how
|w| gets particular values.

\yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
signed quantity in two's complement notation, |-128<=b<128|. This command
changes the current |w|~spacing and moves right by |b|.

\yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is a two-byte-long
parameter, |-32768<=b<32768|.

\yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is a three-byte-long
parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.

\yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is a four-byte-long
parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.

\yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
commands are like the `|w|' commands except that they involve |x| instead
of |w|.

\yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
signed quantity in two's complement notation, |-128<=b<128|. This command
changes the current |x|~spacing and moves right by |b|.

\yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is a two-byte-long
parameter, |-32768<=b<32768|.

\yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is a three-byte-long
parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.

\yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is a four-byte-long
parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.

\yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
The parameter is a signed number in two's complement notation, |-128<=a<128|;
if |a<0|, the reference point actually moves up.

\yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a
two-byte quantity in the range |-32768<=a<32768|.

\yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a
three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.

\yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a
four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.

\yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
this parameterless command will usually suffice, because the same kind of motion
will occur several times in succession; the following commands explain how
|y| gets particular values.

\yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
signed quantity in two's complement notation, |-128<=a<128|. This command
changes the current |y|~spacing and moves down by |a|.

\yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is a two-byte-long
parameter, |-32768<=a<32768|.

\yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is a three-byte-long
parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.

\yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is a four-byte-long
parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.

\yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
are like the `|y|' commands except that they involve |z| instead of |y|.

\yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
signed quantity in two's complement notation, |-128<=a<128|. This command
changes the current |z|~spacing and moves down by |a|.

\yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is a two-byte-long
parameter, |-32768<=a<32768|.

\yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is a three-byte-long
parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.

\yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is a four-byte-long
parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.

\yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
defined by a \\{fnt\_def} instruction, as explained below.

\yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set
|f:=1|, \dots, |f:=63|, respectively.

\yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
numbers in the range |64<=k<256|.

\yskip\hang|fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
command, but large font numbers may prove useful for specifications of
color or texture, or they may be used for special fonts that have fixed
numbers in some external coding scheme.

\yskip\hang|fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
bytes long, so it can be as large as $2^{24}-1$.

\yskip\hang|fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
bytes long; this is for the really big font numbers (and for the negative ones).

\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
programs are being used. \TeX82 generates |xxx1| when a short enough
\.{\\special} appears, setting |k| to the number of bytes being sent. It
is recommended that |x| be a string having the form of a keyword followed
by possible parameters relevant to that keyword.

\yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.

\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.

\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
large. \TeX82 uses |xxx4| when |xxx1| would be incorrect.

\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |0<=k<256|; font definitions will be explained shortly.

\yskip\hang|fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |0<=k<65536|.

\yskip\hang|fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |0<=k<@t$2^{24}$@>|.

\yskip\hang|fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.

\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
Beginning of the preamble; this must come at the very beginning of the
file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.

\yskip\hang|post| 248. Beginning of the postamble, see below.

\yskip\hang|post_post| 249. Ending of the postamble, see below.

\yskip\noindent Commands 250--255 are undefined at the present time.

@ @d set_char_0=0 {typeset character 0 and move right}
@d set1=128 {typeset a character and move right}
@d set_rule=132 {typeset a rule and move right}
@d put1=133 {typeset a character}
@d put_rule=137 {typeset a rule}
@d nop=138 {no operation}
@d bop=139 {beginning of page}
@d eop=140 {ending of page}
@d push=141 {save the current positions}
@d pop=142 {restore previous positions}
@d right1=143 {move right}
@d w0=147 {move right by |w|}
@d w1=148 {move right and set |w|}
@d x0=152 {move right by |x|}
@d x1=153 {move right and set |x|}
@d down1=157 {move down}
@d y0=161 {move down by |y|}
@d y1=162 {move down and set |y|}
@d z0=166 {move down by |z|}
@d z1=167 {move down and set |z|}
@d fnt_num_0=171 {set current font to 0}
@d fnt1=235 {set current font}
@d xxx1=239 {extension to \.{DVI} primitives}
@d xxx4=242 {potentially long extension to \.{DVI} primitives}
@d fnt_def1=243 {define the meaning of a font number}
@d pre=247 {preamble}
@d post=248 {postamble beginning}
@d post_post=249 {postamble ending}
@d undefined_commands==250,251,252,253,254,255

@ The preamble contains basic information about the file as a whole. As
stated above, there are six parameters:
$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
The |i| byte identifies \.{DVI} format; currently this byte is always set
to~2. (Some day we will set |i=3|, when \.{DVI} format makes another
incompatible change---perhaps in 1992.)

The next two parameters, |num| and |den|, are positive integers that define
the units of measurement; they are the numerator and denominator of a
fraction by which all dimensions in the \.{DVI} file could be multiplied
in order to get lengths in units of $10^{-7}$ meters. (For example, there are
exactly 7227 \TeX\ points in 254 centimeters, and \TeX82 works with scaled
points where there are $2^{16}$ sp in a point, so \TeX82 sets |num=25400000|
and $|den|=7227\cdot2^{16}=473628672$.)
@^sp@>

The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the
desired magnification. The actual fraction by which dimensions are
multiplied is therefore $mn/1000d$. Note that if a \TeX\ source document
does not call for any `\.{true}' dimensions, and if you change it only by
specifying a different \.{\\mag} setting, the \.{DVI} file that \TeX\
creates will be completely unchanged except for the value of |mag| in the
preamble and postamble. (Fancy \.{DVI}-reading programs allow users to
override the |mag|~setting when a \.{DVI} file is being printed; this facility
has not been implemented in \.{DVItoLN03}.)

Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
interpreted further. The length of comment |x| is |k|, where |0<=k<256|.

@d id_byte=2 {identifies the kind of \.{DVI} files described here}

@ Font definitions for a given font number |k| contain further parameters
$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
The four-byte value |c| is the check sum that \TeX\ (or whatever program
generated the \.{DVI} file) found in the \.{TFM} file for this font;
|c| should match the check sum of the font found by programs that read
this \.{DVI} file.
@^check sum@>

Parameter |s| contains a fixed-point scale factor that is applied to the
character widths in font |k|; font dimensions in \.{TFM} files and other
font files are relative to this quantity, which is always positive and
less than $2^{27}$. It is given in the same units as the other dimensions
of the \.{DVI} file.  Parameter |d| is similar to |s|; it is the ``design
size,'' and it is given in \.{DVI} units that have not been corrected for
the magnification~|mag| found in the preamble.  Thus, font |k| is to be
used at $|mag|\cdot s/1000d$ times its normal size.

The remaining part of a font definition gives the external name of the font,
which is an ASCII string of length |a+l|. The number |a| is the length
of the ``area'' or directory, and |l| is the length of the font name itself;
the standard local system font area is supposed to be used when |a=0|.
The |n| field contains the area in its first |a| bytes.

Font definitions must appear before the first use of a particular font number.
Once font |k| is defined, it must not be defined again; however, we
shall see below that font definitions appear in the postamble as well as
in the pages, so in this sense each font number is defined exactly twice,
if at all. Like |nop| commands and \\{xxx} commands, font definitions can
appear before the first |bop|, or between an |eop| and a |bop|.

@ The last page in a \.{DVI} file is followed by `|post|'; this command
introduces the postamble, which summarizes important facts that \TeX\ has
accumulated about the file, making it possible to print subsets of the data
with reasonable efficiency. The postamble has the form
$$\vbox{\halign{\hbox{#\hfil}\cr
  |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
  $\langle\,$font definitions$\,\rangle$\cr
  |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
Here |p| is a pointer to the final |bop| in the file. The next three
parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
appeared in the preamble.

Parameters |l| and |u| give respectively the height-plus-depth of the tallest
page and the width of the widest page, in the same units as other dimensions
of the file. These numbers might be used by a \.{DVI}-reading program to
position individual ``pages'' on large sheets of film or paper; however,
the standard convention for output on normal size paper is to position each
page so that the upper left-hand corner is exactly one inch from the left
and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer
software that attempts cleverly to center the output; a fixed position of
the upper left corner is easiest for users to understand and to work with.
Therefore |l| and~|u| are often ignored.

Parameter |s| is the maximum stack depth (i.e., the largest excess of
|push| commands over |pop| commands) needed to process this file. Then
comes |t|, the total number of pages (|bop| commands) present.

The postamble continues with font definitions, which are any number of
\\{fnt\_def} commands as described above, possibly interspersed with |nop|
commands. Each font number that is used in the \.{DVI} file must be defined
exactly twice: Once before it is first selected by a \\{fnt} command, and once
in the postamble.

@ The last part of the postamble, following the |post_post| byte that
signifies the end of the font definitions, contains |q|, a pointer to the
|post| command that started the postamble.  An identification byte, |i|,
comes next; this currently equals~2, as in the preamble.

The |i| byte is followed by four or more bytes that are all equal to
the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of
these trailing bytes, until the total length of the file is a multiple of
four bytes, since this works out best on machines that pack four bytes per
word; but any number of 223's is allowed, as long as there are at least four
of them. In effect, 223 is a sort of signature that is added at the very end.
@^Fuchs, David Raymond@>

This curious way to finish off a \.{DVI} file makes it feasible for
\.{DVI}-reading programs to find the postamble first, on most computers,
even though \TeX\ wants to write the postamble last. Most operating
systems permit random access to individual words or bytes of a file, so
the \.{DVI} reader can start at the end and skip backwards over the 223's
until finding the identification byte. Then it can back up four bytes, read
|q|, and move to byte |q| of the file. This byte should, of course,
contain the value 248 (|post|); now the postamble can be read, so the
\.{DVI} reader discovers all the information needed for typesetting the
pages. Note that it is also possible to skip through the \.{DVI} file at
reasonably high speed to locate a particular page, if that proves
desirable. This saves a lot of time, since \.{DVI} files used in production
jobs tend to be large.

Unfortunately, however, standard \PASCAL\ does not include the ability to
@^system dependencies@>
access a random position in a file, or even to determine the length of a file.
Almost all systems nowadays provide the necessary capabilities, so \.{DVI}
format has been designed to work most efficiently with modern operating systems.
As noted above, \.{DVItoLN03} would need to be modified to limit itself to the
restrictions of standard \PASCAL\ if |random_reading| is defined to be |false|.

@* Font metric data.
The idea behind \.{TFM} files is that typesetting routines like \TeX\
need a compact way to store the relevant information about several
dozen fonts, and computer centers need a compact way to store the
relevant information about several hundred fonts. \.{TFM} files are
compact, and most of the information they contain is highly relevant,
so they provide a solution to the problem.

The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
Since the number of bytes is always a multiple of 4, we could
also regard the file as a sequence of 32-bit words; but \TeX\ uses the
byte interpretation. Note that the bytes are considered to be unsigned
numbers.

@<Glob...@>=
@!tfm_file:byte_file; {a font metric file}

@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
integers that give the lengths of the various subsequent portions
of the file. These twelve integers are, in order:
$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
|@!lf|&length of the entire file, in words;\cr
|@!lh|&length of the header data, in words;\cr
|@!bc|&smallest character code in the font;\cr
|@!ec|&largest character code in the font;\cr
|@!nw|&number of words in the width table;\cr
|@!nh|&number of words in the height table;\cr
|@!nd|&number of words in the depth table;\cr
|@!ni|&number of words in the italic correction table;\cr
|@!nl|&number of words in the lig/kern table;\cr
|@!nk|&number of words in the kern table;\cr
|@!ne|&number of words in the extensible character table;\cr
|@!np|&number of font parameter words.\cr}}$$
They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
|ne<=256|, and
$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
and as few as 0 characters (if |bc=ec+1|).

Incidentally, when two or more 8-bit bytes are combined to form an integer of
16 or more bits, the most significant bytes appear first in the file.
This is called BigEndian order.

@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
arrays having the informal specification
$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
\vbox{\halign{\hfil\\{#}&$\,:\,$\arr#\hfil\cr
header&|[0..lh-1]stuff|\cr
char\_info&|[bc..ec]char_info_word|\cr
width&|[0..nw-1]fix_word|\cr
height&|[0..nh-1]fix_word|\cr
depth&|[0..nd-1]fix_word|\cr
italic&|[0..ni-1]fix_word|\cr
lig\_kern&|[0..nl-1]lig_kern_command|\cr
kern&|[0..nk-1]fix_word|\cr
exten&|[0..ne-1]extensible_recipe|\cr
param&|[1..np]fix_word|\cr}}$$
The most important data type used here is a |@!fix_word|, which is
a 32-bit representation of a binary fraction. A |fix_word| is a signed
quantity, with the two's complement of the entire word used to represent
negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
the smallest is $-2048$. We will see below, however, that all but one of
the |fix_word| values will lie between $-16$ and $+16$.

@ The first data array is a block of header information, which contains
general facts about the font. The header must contain at least two words,
and for \.{TFM} files to be used with Xerox printing software it must
contain at least 18 words, allocated as described below. When different
kinds of devices need to be interfaced, it may be necessary to add further
words to the header block.

\yskip\hang|header[0]| is a 32-bit check sum that \TeX\ will copy into the
\.{DVI} output file whenever it uses the font.  Later on when the \.{DVI}
file is printed, possibly on another computer, the actual font that gets
used is supposed to have a check sum that agrees with the one in the
\.{TFM} file used by \TeX. In this way, users will be warned about
potential incompatibilities. (However, if the check sum is zero in either
the font file or the \.{TFM} file, no check is made.)  The actual relation
between this check sum and the rest of the \.{TFM} file is not important;
the check sum is simply an identification number with the property that
incompatible fonts almost always have distinct check sums.
@^check sum@>

\yskip\hang|header[1]| is a |fix_word| containing the design size of the
font, in units of \TeX\ points (7227 \TeX\ points = 254 cm).  This number
must be at least 1.0; it is fairly arbitrary, but usually the design size
is 10.0 for a ``10 point'' font, i.e., a font that was designed to look
best at a 10-point size, whatever that really means. When a \TeX\ user
asks for a font `\.{at} $\delta$ \.{pt}', the effect is to override the
design size and replace it by $\delta$, and to multiply the $x$ and~$y$
coordinates of the points in the font image by a factor of $\delta$
divided by the design size.  {\sl All other dimensions in the\/\ \.{TFM}
file are |fix_word|\kern-1pt\ numbers in design-size units.} Thus, for example,
the value of |param[6]|, one \.{em} or \.{\\quad}, is often the |fix_word|
value $2^{20}=1.0$, since many fonts have a design size equal to one em.
The other dimensions must be less than 16 design-size units in absolute
value; thus, |header[1]| and |param[1]| are the only |fix_word| entries in
the whole \.{TFM} file whose first byte might be something besides 0 or
255.  @^design size@>

\yskip\hang|header[2..11]|, if present, contains 40 bytes that identify
the character coding scheme. The first byte, which must be between 0 and
39, is the number of subsequent ASCII bytes actually relevant in this
string, which is intended to specify what character-code-to-symbol
convention is present in the font.  Examples are \.{ASCII} for standard
ASCII, \.{TeX text} for fonts like \.{cmr10} and \.{cmti9}, \.{TeX math
extension} for \.{cmex10}, \.{XEROX text} for Xerox fonts, \.{GRAPHIC} for
special-purpose non-alphabetic fonts, \.{UNSPECIFIED} for the default case
when there is no information.  Parentheses should not appear in this name.
(Such a string is said to be in {\mc BCPL} format.)
@^coding scheme@>

\yskip\hang|header[12..16]|, if present, contains 20 bytes that name the
font family (e.g., \.{CMR} or \.{HELVETICA}), in {\mc BCPL} format.
This field is also known as the ``font identifier.''
@^family name@>
@^font identifier@>

\yskip\hang|header[17]|, if present, contains a first byte called the
|seven_bit_safe_flag|, then two bytes that are ignored, and a fourth byte
called the |face|. If the value of the fourth byte is less than 18, it has
the following interpretation as a ``weight, slope, and expansion'':  Add 0
or 2 or 4 (for medium or bold or light) to 0 or 1 (for roman or italic) to
0 or 6 or 12 (for regular or condensed or extended).  For example, 13 is
0+1+12, so it represents medium italic extended.  A three-letter code
(e.g., \.{MIE}) can be used for such |face| data.

\yskip\hang|header[18..@twhatever@>]| might also be present; the individual
words are simply called |header[18]|, |header[19]|, etc., at the moment.

@ Next comes the |char_info| array, which contains one |char_info_word|
per character. Each |char_info_word| contains six fields packed into
four bytes as follows.

\yskip\hang first byte: |width_index| (8 bits)\par
\hang second byte: |height_index| (4 bits) times 16, plus |depth_index|
  (4~bits)\par
\hang third byte: |italic_index| (6 bits) times 4, plus |tag|
  (2~bits)\par
\hang fourth byte: |remainder| (8 bits)\par
\yskip\noindent
The actual width of a character is |width[width_index]|, in design-size
units; this is a device for compressing information, since many characters
have the same width. Since it is quite common for many characters
to have the same height, depth, or italic correction, the \.{TFM} format
imposes a limit of 16 different heights, 16 different depths, and
64 different italic corrections.

Incidentally, the relation |width[0]=height[0]=depth[0]=italic[0]=0|
should always hold, so that an index of zero implies a value of zero.
The |width_index| should never be zero unless the character does
not exist in the font, since a character is valid if and only if it lies
between |bc| and |ec| and has a nonzero |width_index|.

@ The remaining details of the format of \.{TFM} files need not concern us
further; the interested reader is referred to Knuth's standard \.{TFtoPL}
@^Knuth, D.~E.@>
@.TFtoPL@>
program for the whole picture.

@* Input from binary files.
We have seen that a \.{DVI} file is a sequence of 8-bit bytes. The bytes
appear physically in what is called a `|packed file of 0..255|'
in \PASCAL\ lingo.

Packing is system dependent, and many \PASCAL\ systems fail to implement
such files in a sensible way (at least, from the viewpoint of producing
good production software).  For example, some systems treat all
byte-oriented files as text, looking for end-of-line marks and such
things. Therefore some system-dependent code is often needed to deal with
binary files, even though most of the program in this section of
\.{DVItoLN03} is written in standard \PASCAL.
@^system dependencies@>

One common way to solve the problem is to consider files of |integer|
numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to
a sequence of four bytes $(a,b,c,d)$ using the following code, which
avoids the controversial integer division of negative numbers:
$$\vbox{\halign{#\hfil\cr
|if x>=0 then a:=x div @'100000000|\cr
|else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr
\quad|end|\cr
|x:=x mod @'100000000;|\cr
|b:=x div @'200000; x:=x mod @'200000;|\cr
|c:=x div @'400; d:=x mod @'400;|\cr}}$$
The four bytes are then kept in a buffer and output one by one. (On 36-bit
computers, an additional division by 16 is necessary at the beginning.
Another way to separate an integer into four bytes is to use/abuse
\PASCAL's variant records, storing an integer and retrieving bytes that are
packed in the same place; {\sl caveat implementor!\/}) It is also desirable
in some cases to read a hundred or so integers at a time, maintaining a
larger buffer.

Although plain \.{DVItype} sticks to simple \PASCAL, for reasons of clarity,
(even if such simplicity is sometimes unrealistic), we do things rather
differently under Vax/VMS, reading each file one disk block at a time.

@d VAX_block_length=512

@<Types...@>=
@!eight_bits=[byte] 0..255; {unsigned one-byte quantity}
@!sixteen_bits=[word] 0..65535; {unsigned two-byte quantity}
@!byte_block=packed array [0..VAX_block_length-1] of eight_bits;
@!byte_file=packed file of byte_block;


@ To prepare these files for input, we |reset| them. An extension of
\PASCAL\ is needed in the case of |tfm_file|, since we want to associate
it with external files whose names are specified dynamically (i.e., not
known at compile time). The following code uses the VAX-\PASCAL\ |open|
statement; where |open(f,s)| opens the file associated with file variable |f|
using the name provided by string variable |s|; once the file variable has
been associated with the correctly named file, the |reset| statement positions
the file for reading in the usual manner.
In the case of opening the |tfm_file|, we firstly close any earlier
one which may already have been opened. If |eof(f)| is true immediately after
the file has been opened, we assume that no file named |s| is accessible.
@^system dependencies@>

To improve the visual appearance of various VAX-\PASCAL\ file manipulation
statements, the following definition is used to represent the use of the
`named parameter' passing mechanism which instructs that any errors detected
shall be ignored, rather than causing a run-time error.

@d VAX_continue==@=error@>:=@=continue@>

@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
begin reset(dvi_file);
dvi_count:=0; cur_block:=0;
cur_loc:=0;
end;
@#
procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
  var @!trimmed_name:file_spec; {Holds trimmed version of |cur_name|}
  @!i,@!j:0..name_length; {Indices into |cur_name|}
begin close(tfm_file,VAX_continue);
trimmed_name:=''; {Initialize to empty string}
i:=name_length;
while cur_name[i]=' ' do decr(i); {we know that the name isn't all spaces!}
for j:=1 to i do trimmed_name:=trimmed_name+cur_name[j];
open(tfm_file,cur_name,@=readonly@>,VAX_continue);
if status(tfm_file)<>0 then
  abort('Font not loaded, TFM file ',trimmed_name,' can''t be opened');
@:fatal error Font not loaded TFM file}{\qquad\.{TFM file can't be opened}@>
reset(tfm_file,VAX_continue);
tfm_count:=0;
end;

@ If you looked carefully at the preceding code, you probably asked,
``What are |cur_loc| and |cur_name|?'' Good question. They're global
variables: |cur_loc| is the number of the byte about to be read next from
|dvi_file|, and |cur_name| is a string variable that will be set to the
current font metric file name before |open_tfm_file| is called.  When the
\.{DVI} file is first |open|ed, we'll make use of the |@=user_action@>| facility
of the VAX |open| procedure to note that actual file length in |dvi_size|.
This can then be used as the value of the |dvi_length| function.

Since we read a VAX/RMS blockfull of bytes from the \.{DVI} file, variable
|dvi_count| allows us to keep track of which byte of the current block is
the next to be ``read''.  Similarly, |tfm_count| tracks the current byte of
the block read from |tfm_file|.   |cur_block| tracks which particular block
of the whole file is currently in the buffer associated with |dvi_file|;
this is used when |random_reading| is used to skip around in the file.  No
analogous variable is required for the |tfm_file|.

@<Glob...@>=
@!cur_loc:integer; {where we are about to look, in |dvi_file|}
@!cur_name:packed array[1..name_length] of char; {external name,
  with no lower case letters}
@!dvi_size:integer; {Actual length of \.{DVI} file, in bytes}
@!dvi_count:integer; {number of bytes read from current block of |dvi_file|}
@!cur_block:integer; {relative position of current block of |dvi_file|}
@!tfm_count:integer; {number of bytes read from current block of |tfm_file|}

@ It turns out to be convenient to read four bytes at a time, when we are
inputting from \.{TFM} files. The input goes into global variables
|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
the fourth.

@<Glob...@>=
@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once}

@ The |read_tfm_word| procedure sets |b0| through |b3| to the next
four bytes in the current \.{TFM} file.
@^system dependencies@>

@d read_tfm_file(#)==begin
        if tfm_count=VAX_block_length then begin
                get(tfm_file,VAX_continue); tfm_count:=0;
                end;
        #:=tfm_file^[tfm_count];
        incr(tfm_count);
        end

@p procedure read_tfm_word;
begin read_tfm_file(b0); read_tfm_file(b1);
read_tfm_file(b2); read_tfm_file(b3);
end;

@ We shall use another set of simple functions to read the next byte or
bytes from |dvi_file|. There are seven possibilities, each of which is
treated as a separate function in order to minimize the overhead for
subroutine calls.
@^system dependencies@>

Things aren't quite as simple as that, because when processing |dvi|
commands to set characters on the page, it may prove necessary to take
further |dvi| commands from a sequence read from a virtual font (\\{q.v.}).
Therefore, these routines have the option of reading |dvi| bytes from
|vf[vf_take]|.  When reading |dvi| bytes from the \.{DVI} file, |vftake<0|.

(\\{N.B.} |vf_take| and |vf_ptr| will be dealt with \&{much} later.)

@d read_dvi_file(#)==if vf_take<0 then
        begin
        while dvi_count>=VAX_block_length do begin
                get(dvi_file,VAX_continue);
                dvi_count:=dvi_count-VAX_block_length; incr(cur_block);
                end;
        #:=dvi_file^[dvi_count];
        incr(dvi_count); incr(cur_loc)
        end
        else
        begin {take ``input'' from stored sequence}
          if vf_take=vf_ptr then abort('no more virtual font bytes');
@:fatal error no more virtual font bytes}{\quad\.{no more virtual font bytes}@>
          #:=vf[vf_take]; incr(vf_take)
        end

@p function get_byte:integer; {returns the next byte, unsigned}
var b:eight_bits;
begin if eof(dvi_file) then get_byte:=0
else  begin read_dvi_file(b); get_byte:=b;
  end;
end;
@#
function signed_byte:integer; {returns the next byte, signed}
var b:eight_bits;
begin read_dvi_file(b);
if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
end;
@#
function get_two_bytes:integer; {returns the next two bytes, unsigned}
var a,@!b:eight_bits;
begin read_dvi_file(a); read_dvi_file(b);
get_two_bytes:=a*256+b;
end;
@#
function signed_pair:integer; {returns the next two bytes, signed}
var a,@!b:eight_bits;
begin read_dvi_file(a); read_dvi_file(b);
if a<128 then signed_pair:=a*256+b
else signed_pair:=(a-256)*256+b;
end;
@#
function get_three_bytes:integer; {returns the next three bytes, unsigned}
var a,@!b,@!c:eight_bits;
begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c);
get_three_bytes:=(a*256+b)*256+c;
end;
@#
function signed_trio:integer; {returns the next three bytes, signed}
var a,@!b,@!c:eight_bits;
begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c);
if a<128 then signed_trio:=(a*256+b)*256+c
else signed_trio:=((a-256)*256+b)*256+c;
end;
@#
function signed_quad:integer; {returns the next four bytes, signed}
var a,@!b,@!c,@!d:eight_bits;
begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c); read_dvi_file(d);
if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
end;

@ Finally we come to the routines that are used only if |random_reading| is
|true|. The driver program below needs two such routines: |dvi_length| should
compute the total number of bytes in |dvi_file|, possibly also
causing |eof(dvi_file)| to be true; and |move_to_byte(n)|
should position |dvi_file| so that the next |get_byte| will read byte |n|,
starting with |n=0| for the first byte in the file.
@^system dependencies@>

Such routines are, of course, highly system dependent. They are implemented
in this program by

\yskip\hang(1) utilizing the optional |user_action| parameter of the |open|
procedure, this allows us to determine the length of the file when it is
first opened; and

\yskip\hang(2), using the VAX-\PASCAL\ |find| procedure to read a specific
block of the file, and positioning to the correct byte by resetting the
|byte_block| pointer appropriately.

\noindent We also make extensive use of VAX-\PASCAL's |varying array|
extension, which
facilitates the handling of variable length character strings; without them, it
would be necessary to keep track of the length of each such string, in the
fashion used by \.{DVItype} to store font names.

The VAX-\PASCAL\ |find(f,block)| procedure reads (into file variable
|f|) the specified record of the file; these are numbered from 1 upwards,
and, since we are working with block-oriented files, each record consists of
an entire disk block.

@f varying==array
@d VAX_find_block==@= find@>

@p function dvi_length:integer;
begin dvi_length:=dvi_size; if dvi_size<0 then
        abort('Internal error (no dvi_length)');
@:fatal error internal error no dvi}{\qquad\.{(no dvi_length)}@>
end;
@#
procedure move_to_byte(n:integer);
begin if n div VAX_block_length <> cur_block then
        begin cur_block:=n div VAX_block_length;
                VAX_find_block(dvi_file,cur_block+1,VAX_continue)
        end;
        dvi_count:=n mod VAX_block_length; cur_loc:=n;
end;

@* Reading the font information.
\.{DVI} file format does not include information about character widths, since
that would tend to make the files a lot longer. But a program that reads
a \.{DVI} file is supposed to know the widths of the characters that appear
in \\{set\_char} commands. Therefore \.{DVItoLN03} looks at the font metric
(\.{TFM}) files for the fonts that are involved.
@.TFM {\rm files}@>

The character-width data appears also in other files (e.g., in \.{GF} files
that specify bit patterns for digitized characters);
thus, it is usually possible for \.{DVI} reading programs to get by with
accessing only one file per font. \.{DVItype} has a comparatively easy
task in this regard, since it needs only a few words of information from
each font; the \.{DVItoLN03} program has to go to some pains to
deal with complications that arise when a large number of large font files
all need to be accessed simultaneously.

@ \.{DVItype} needed to know only two things about a
given character |c| in a given font |f|: (1)~Is |c| a legal character
in~|f|? (2)~If so, what is the width of |c|? It also needed to know the
symbolic name of each font, so it could be printed out, and to know
the approximate size of inter-word spaces in each font.

For \.{DVItoLN03}, some further information is required, such as which
character in a downloaded font is to be used to image character |c| in font
|f|, and whether this imaging can be performed by the LN03 itself (from
stored downloaded fonts) or whether the glyph needs to be imaged by
downloading a bitmapped representation each time it occurs, or even replacing
it by a solid rule if no suitable file of bitmaps is available.

The answers to these questions appear implicitly in the following data
structures. The current number of known fonts is |nf|. Each known font has
an internal number |f|, where |0<=f<nf|; the external number of this font,
i.e., its font identification number in the \.{DVI} file, is |font_num[f]|,
and the external name of this font is the string that occupies positions
|font_name[f]| through |font_name[f+1]-1| of the array |names|. The latter
array consists of |ASCII_code| characters, and |font_name[nf]| is its first
unoccupied position.

Various metrical data is saved for comparison with values to be read later
from the file(s) containing the characters' bitmaps: |font_check_sum|,
|font_scaled_size| and |font_design_size|.
A horizontal motion in the range |-4*font_space[f]<h<font_space[f]|
will be treated as a `kern' that merely advances the program's conception of
physical horizontal position; larger movements will result in computation of a
freshly rounded position, thus eliminating cumulative errors which might
otherwise lead to unsightly character placement on the page.

The legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive; more
precisely, a given character |c| is valid in font |f| if and only if
|font_bc[f]<=c<=font_ec[f]| and |char_width(f)(c)<>invalid_width|.
To cut down on page faulting with small working sets, the dimensions of each
character are held within the |glyph_map| structure (see following section),
so that all information relating to a particular glyph is held in one
contiguous section of memory: the original \.{DVItype}, and earlier versions
of \.{DVItoLN03}, used to have separate arrays |width[0..max_glyphs]|, etc.,
which could require references to locations separated by |max_glyphs|
integers, making inefficient use of virtual memory.

To obviate reloading of fonts with identical names and scaling, a further
array |font_map[f]| ordinarily holds the value |f|, but can point to a
different set of metrics if such multiple loading has been avoided.

Virtual fonts will almost certainly introduce different physical fonts; such
fonts are given a relative reference number internally, but we will want to
supply a value for the |font_num| array that's unique to each such font.
Therefore we keep track of the highest font number allocated by \TeX\ and
then use this as a base value when reading virtual fonts.

@d invalid_width==@'17777777777

@<Glob...@>=
@!nf:0..max_fonts; {the number of known fonts}
@!font_num:array [0..max_fonts] of integer; {external font numbers}
@!font_name:array [0..max_fonts] of 0..name_size; {starting positions
  of external font names}
@!names:array [0..name_size] of ASCII_code; {characters of names}
@!font_check_sum:array [0..max_fonts] of integer; {check sums}
@!font_scaled_size:array [0..max_fonts] of integer; {scale factors}
@!font_design_size:array [0..max_fonts] of integer; {design sizes}
@!font_space:array [0..max_fonts] of integer; {boundary between ``small''
  and ``large'' spaces}
@!font_bc:array [0..max_fonts] of integer; {beginning characters in fonts}
@!font_ec:array [0..max_fonts] of integer; {ending characters in fonts}
@!font_map:array [0..max_fonts] of 0..max_fonts; {internal cross-reference}
@!highest_font:0..max_fonts;

@ An array |glyphs| has one element for each of the potential characters in
each font.  Each element is a record of type |glyph_info| (defined below).
When a font definition is first read, during the gathering of font usage
statistics in the first pass, the row of this array corresponding to the
font being defined is set to indicate that every character is |unused|. As
any character in this font is found in a wanted page during that first pass,
the usage is noted.  Later revisions take place when mapping characters from
\TeX's fonts to those dowloaded to the printer.

During the second pass through the wanted pages, the character codes in
|glyphs| are used to generate the \.{LN3} file.

|glyph_map(f)(c)=glyphs[glyph_base[f]+c]| permits us access to information
relating to an individual glyph; fields of this record, named |width|, |height|
and |depth| are accessible through macro
|char_width(f)(c)=glyphs[glyph_base[f]+c].width| and its analogues; this works
because |glyph_base[f]| contains the index into |glyphs| at which character~0
appears (or would appear, if |font_bc[f]>0|). |glyph_ptr| is the first unused
position of the |glyphs| array.

@d char_glyph_end(#)==#]
@d glyph_map(#)==glyphs[glyph_base[#]+char_glyph_end
@d char_width_end(#)==#].width
@d char_height_end(#)==#].height
@d char_depth_end(#)==#].depth
@d char_width(#)==glyphs[glyph_base[#]+char_width_end
@d char_height(#)==glyphs[glyph_base[#]+char_height_end
@d char_depth(#)==glyphs[glyph_base[#]+char_depth_end

@<Glob...@>=
@!glyphs: array [0..max_glyphs] of glyph_info;
@!glyph_base : array [0..max_fonts] of integer; {index into |glyphs|}
@!glyph_ptr : 0..max_glyphs; {index into |glyphs|, |width|, |height| and
|depth|}

@ The array |glyphs| contains elements whose fields are used as follows:

\yskip\centerline{\vbox{\offinterlineskip\hrule
  \halign{\vrule#&\strut\quad#\hfil\quad&\vrule#&\quad#\hfil\quad&\vrule#\cr
      height2pt&\omit&&\omit&\cr
      &\hfil Field Name\quad&&Used for&\cr
      height2pt&\omit&&\omit&\cr
      \noalign{\hrule}
      height2pt&\omit&&\omit&\cr
      &|width|&&Character width in \.{DVI} units&\cr
      &|height|&&Character height in \.{DVI} units&\cr
      &|depth|&&Character depth in \.{DVI} units&\cr
      &|font_code|&&Number of downloaded LN03 font containing glyph&\cr
      &|char_code|&&LN03 character code to access glyph&\cr
      &|loaded|&&Indicates status of glyph, as below&\cr
      height2pt&\omit&&\omit&\cr
    }\hrule
  }
}

The values that may be assumed by the |loaded| field are as follows:

\item{$\bullet$} when a font is first met, all characters are set with
the value |unused|;
\item{$\bullet$} as the document is scanned, whenever a particular glyph
is used this field is set to |wanted|;
\item{$\bullet$} other values are set once the wanted pages have been
scanned, and the character font downloads created.

\noindent Further values for the |loaded| field, and other fields, will be
defined for accessing virtual fonts.

@<Types...@>==
@!download_status = (@!unused,@!wanted@|@<Other values of |download_status|@>@+);@/
@!glyph_info =
  packed record
    @!width:integer; {character width, in \.{DVI} units}
    @!height:integer; {character height, in \.{DVI} units}
    @!depth:integer; {character depth, in \.{DVI} units}
    @!char_code : eight_bits;
    @!loaded : download_status;
    @!font_code : 0..max_fonts;
    @<Other fields of |glyph_info|@>@;
  end;


@ @<Set init...@>=
nf:=0; glyph_ptr:=0; font_name[0]:=0; font_space[0]:=0; font_map[0]:=0;
highest_font:=0;

@ It is, of course, a simple matter to print the name of a given font.

@p procedure print_font(@!f:integer); {|f| is an internal font number}
var k:0..name_size; {index into |names|}
begin if f=nf then print('UNDEFINED!')
@.UNDEFINED@>
else  begin for k:=font_name[f] to font_name[f+1]-1 do
    print(xchr[names[k]]);
  end;
end;

@ Auxiliary arrays |in_width|, |in_height| and |in_depth| are used to hold
the relevant sections of the |tfm_file| as they are
input. The global variable |tfm_check_sum| is set to the check sum that
appears in the current \.{TFM} file.

@<Glob...@>=
@!in_width:array[0..255] of integer; {\.{TFM} width data in \.{DVI} units}
@!in_height:array[0..15] of integer; {\.{TFM} height data in \.{DVI} units}
@!in_depth:array[0..15] of integer; {\.{TFM} depth data in \.{DVI} units}
@!tfm_check_sum:integer; {check sum found in |tfm_file|}

@ Here is a procedure that absorbs the necessary information from a
\.{TFM} file, assuming that the file has just been successfully reset
so that we are ready to read its first byte. (A complete description of
\.{TFM} file format appears in the documentation of \.{TFtoPL} and will
not be repeated here.) The procedure does not check the \.{TFM} file
for validity, nor does it give explicit information about what is
wrong with a \.{TFM} file that proves to be invalid; \.{DVI}-reading
programs need not do this, since \.{TFM} files are almost always valid,
and since the \.{TFtoPL} utility program has been specifically designed
to diagnose \.{TFM} errors. The procedure simply returns |false| if it
detects anything amiss in the \.{TFM} data.

There is a parameter, |z|, which represents the scaling factor being
used to compute the font dimensions; it must be in the range $0<z<2^{27}$.

@p function in_TFM(@!z:integer):boolean; {input \.{TFM} data or return |false|}
label 9997, {go here when the format is bad}
  9998,  {go here when the information cannot be loaded}
  9999;  {go here to exit}
var k:integer; {index for loops}
@!lh:integer; {length of the header data, in four-byte words}
@!nw:integer; {number of words in the width table}
@!nh:integer; {number of words in the height table}
@!nd:integer; {number of words in the depth table}
@!gp:0..max_glyphs; {new value of |glyph_ptr| after successful input}
@!alpha,@!beta:integer; {quantities used in the scaling computation}
begin @<Read past the header data; |goto 9997| if there is a problem@>;
@<Store character-width indices at the end of the |width| table@>;
@<Read and convert the width values, setting up the |in_width| table@>;
@<Move the widths from |in_width| to |width|, and append |pixel_width| values@>;
glyph_ptr:=gp; in_TFM:=true; goto 9999;
9997: error('---not loaded, TFM file is bad');
@.TFM file is bad@>
9998: in_TFM:=false;
9999: end;

@ @<Read past the header...@>=
read_tfm_word; lh:=b2*256+b3;
read_tfm_word; font_bc[nf]:=b0*256+b1; font_ec[nf]:=b2*256+b3;
if font_ec[nf]<font_bc[nf] then font_bc[nf]:=font_ec[nf]+1;
@<Mark all glyphs of font |nf| as |unused|@>;
{now |gp=glyph_ptr+font_ec[nf]-font_bc[nf]+1|}
read_tfm_word; nw:=b0*256+b1;
if (nw=0)or(nw>256) then goto 9997;
nh:=b2*256+b3;
if (nh=0)or(nh>16) then goto 9997;
read_tfm_word; nd:=b0*256+b1;
if (nd=0)or(nd>16) then goto 9997;
for k:=2 to 3+lh do
  begin if eof(tfm_file) then goto 9997;
  read_tfm_word;
  if k=4 then
    if b0<128 then tfm_check_sum:=((b0*256+b1)*256+b2)*256+b3
    else tfm_check_sum:=(((b0-256)*256+b1)*256+b2)*256+b3;
  end;

@ Whenever the program reads a \.{TFM} file, either during the main
processing loop, or when such files are read in whilst mapping virtual fonts
to physical ones, the |glyph_map| row corresponding to the physical file is
preset to indicate that none of the glyphs are used, and also to signify
that, in the absence of further information, the characters are taken from
the current font.  This little segment of code used to appear in-line within
procedure |in_TFM|.

We also indicate here that no glyphs from the font as a whole are currently
used.  Once a glyph is referenced (either directly, or from within a
character packet in a virtual font), we'll mark the font as |wanted|, thus
indicating (so far as we currently know) the font \\{is} physical; this may
be changed later, if the font is found to be virtual. This information is
stored in yet another array, |font_type|.

@<Mark all glyphs of font |nf| as |unused|@>=
font_type[nf]:=unused;
if glyph_ptr+font_ec[nf]-font_bc[nf]+1 > max_glyphs then
  capacity_exceeded('too many glyphs: ',glyph_ptr+font_ec[nf]-font_bc[nf]+1:1,
        ' > ',max_glyphs:1);
@:capacity exceeded too many glyphs}{\quad\.{too many glyphs}@>
glyph_base[nf]:=glyph_ptr-font_bc[nf];
gp:=glyph_ptr+font_ec[nf]-font_bc[nf]+1;
for m:= glyph_ptr to gp-1 do
    with glyphs[m] do
    begin
        loaded := unused;
        font_code := nf
    end

@ At this point, we've arrived at the start of the |char_info| section of
the |tfm_file|; from each 32-bit word, we extract the indices into the
|width|, |height| and |depth| sections of the file.
@<Store character-width indices...@>=
if gp>0 then for k:=glyph_ptr to gp-1 do
with glyphs[k] do
  begin read_tfm_word;
  if b0>nw then goto 9997;
  width:=b0;
  if (b1 div 16)>nh then goto 9997;
  height:=b1 div 16;
  if (b1 mod 16)>nd then goto 9997;
  depth:=b1 mod 16;
  end;

@ The most important part of |in_TFM| is the width computation, which
involves multiplying the relative widths in the \.{TFM} file by the
scaling factor in the \.{DVI} file. This fixed-point multiplication
must be done with precisely the same accuracy by all \.{DVI}-reading programs,
in order to validate the assumptions made by \.{DVI}-writing programs
like \TeX82.

Let us therefore summarize what needs to be done. Each width in a \.{TFM}
file appears as a four-byte quantity called a |fix_word|.  A |fix_word|
whose respective bytes are $(a,b,c,d)$ represents the number
$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
(No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
dimension must be less than 16.)  We want to multiply this quantity by the
integer~|z|, which is known to be less than $2^{27}$. Let $\alpha=16z$.
If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
$d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
16, to obtain a multiplier less than $2^{23}$, and we can compensate for
this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
$\beta=2^{4-e}$; we shall compute
$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
or the same quantity minus $\alpha$ if $a=255$.  This calculation must be
done exactly, for the reasons stated above; the following program does the
job in a system-independent way, assuming that arithmetic is exact on
numbers less than $2^{31}$ in magnitude.

@<Read and convert the width values...@>=
@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
for k:=0 to nw-1 do
  begin read_tfm_word;
  in_width[k]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
  if b0>0 then if b0<255 then goto 9997
    else in_width[k]:=in_width[k]-alpha;
  end;
@<Read and convert the height and depth values@>

@ Similarly, we store away the sets of distinct character heights and
depths; we don't need to convert |z| again.

@<Read and convert the height and depth values@>=
for k:=0 to nh-1 do
  begin read_tfm_word;
  in_height[k]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
  if b0>0 then if b0<255 then goto 9997
    else in_height[k]:=in_height[k]-alpha;
  end;
for k:=0 to nd-1 do
  begin read_tfm_word;
  in_depth[k]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
  if b0>0 then if b0<255 then goto 9997
    else in_depth[k]:=in_depth[k]-alpha;
  end

@ @<Replace |z|...@>=
begin alpha:=16*z; beta:=16;
while z>=@'40000000 do
  begin z:=z div 2; beta:=beta div 2;
  end;
end

@ A \.{DVI}-reading program usually works with font files instead of
\.{TFM} files, so \.{DVItype} is atypical in that respect. \.{DVItoLN03}
actually checks information in the \.{TFM} file, \&{and} also reads an
appropriate \.{PXL} file.  Font files
should, however, contain exactly the same character width data that is
found in the corresponding \.{TFM}s; check sums are used to help
ensure this. In addition, font files usually also contain the widths of
characters in pixels, since the device-independent character widths of
\.{TFM} files are generally not perfect multiples of pixels.

The |pixel_width| field of the |glyph_map| contains this information; when
|glyphs[k].width| is the device-independent width of some character in
\.{DVI} units, |glyphs[k].pixel_width| is the corresponding width of that
character in an actual font. The macro |char_pixel_width| is set up to be
analogous to |char_width|.

Similarly, |pixel_height| and |pixel_depth| are available; these are used
for replacing the glyph for a character for which the required bitmaps are
unavailable by the appropriately-sized rule.

@d pixel_width_end(#)==#].pixel_width
@d pixel_height_end(#)==#].pixel_height
@d pixel_depth_end(#)==#].pixel_depth
@d char_pixel_width(#)==glyphs[glyph_base[#]+pixel_width_end
@d char_pixel_height(#)==glyphs[glyph_base[#]+pixel_height_end
@d char_pixel_depth(#)==glyphs[glyph_base[#]+pixel_depth_end

@<Other fields of |glyph_info|@>=
@!pixel_width, @!pixel_height, @!pixel_depth : integer;

@ The following variables are used to hold the conversion factors for
translating device-independent units into device pixels.

@<Glob...@>=
@!conv:real; {converts \.{DVI} units to pixels}
@!true_conv:real; {converts unmagnified \.{DVI} units to pixels}
@!numerator,@!denominator:integer; {stated conversion ratio}
@!mag:integer; {magnification factor times 1000}

@ The following code computes pixel widths by simply rounding the \.{TFM}
widths to the nearest integer number of pixels, based on the conversion factor
|conv| that converts \.{DVI} units to pixels. However, such a simple
formula will not be valid for all fonts, and it will often give results that
are off by $\pm1$ when a low-resolution font has been carefully
hand-fitted. For example, a font designer often wants to make the letter `m'
a pixel wider or narrower in order to make the font appear more consistent.
\.{DVI}-to-printer programs should therefore input the correct pixel width
information from font files whenever there is a chance that it may differ.
A warning message may also be desirable in the case that at least one character
is found whose pixel width differs from |conv*width| by more than a full pixel.
@^system dependencies@>
(And similarly for the height and depth values.)

@d pixel_round(#)==round(conv*(#))

@<Move the widths from |in_width| to |width|, and append |pixel_width| values@>=
if in_width[0]<>0 then goto 9997; {the first width should be zero}
if gp>0 then for k:=glyph_ptr to gp-1 do
with glyphs[k] do
begin
  if width=0 then
    begin width:=invalid_width; pixel_width:=0;
    end
  else  begin width:=in_width[width];
    pixel_width:=pixel_round(width);
    end;
  height:=in_height[height];
  pixel_height:=pixel_round(height);
  depth:=in_depth[depth];
  pixel_depth:=pixel_round(depth)
end

@* Output to the terminal. Whilst DVItoLN03 is processing \.{DVI} files, it
reports its progress by outputting various messages to the terminal: in
particular, when processing each page, it reports the page number, enclosed in
`\.{[]}' brackets.  Under Vax/VMS, output to the terminal is not normally
visible until |writeln| is called, since the operating system buffers output
until a line is complete.  However, this would mean, for example, that output
would only appear as each line was filled.

We therefore arrange to |open| the |output| file (synonymous with |term_out|)
specifying null carriage control; output may then be made to appear by calling
|write_ln|, but this will not give the usual accompanying newline.  To start
printing on a newline, the character sequence |crlf| must be output
explicitly.

The |term_out| file is used for terminal output.
@^system dependencies@>
The number of characters already output to a terminal line is counted
in |term_offset| so that a suitable line break may be chosen when the line
becomes ``full'.

Messages giving page number strings being processed are contained in a
variable of type |file_spec|, because some messages may contain VAX/VMS file
names.

@d term_out==output {the terminal, considered as an output file}

@<Glob...@>=
  @!term_offset : 0..line_length;

@ When each page is processed, the page number (as generated by \TeX) is written
out to the user's terminal, enclosed in `\.{[]}' brackets.  The procedure
|monitor| ensures that such output does not overlap the right-margin.  It
does this by counting the characters already output to the line; up to
|line_length| characters will be output.  We use here the VAX-\PASCAL-specific
function |length| which returns the number of characters in a |varying of char|
which are actually occupied.

@d VAX_length==@= length@>

@p procedure monitor(s : file_spec);
  begin if VAX_length(s)+2>line_length-term_offset then {Line is fullish!}
    begin write_ln(term_out,crlf); term_offset:=0; end;
    if (term_offset>0) and (s[1]='[') then      {Space before a |'['|}
    begin write(term_out,' '); incr(term_offset); end;
    write_ln(term_out,s);
    term_offset:=term_offset+VAX_length(s)
  end;

@*Displaying error messages.  Later we shall make extensive use of the
following definitions, particularly when scanning the \.{DVI} file.  However,
they are defined up here so that warnings may be issued by the code that
follows.  Normally (when invoked from within |do_page|) the variable |a| will
contain the byte number within the \.{DVI} file at which a problem was
encountered; however, to give |show| something to work with when these macros
are used elsewhere, we also define a constant of that name, which will thus
always be within scope.

@d show(#)==begin
    if a<0 then print_ln(#) else print_ln('[',a:1,'] ',#);
    if term_offset>0 then write_ln(crlf);
    write_ln(term_out,#,crlf); term_offset:=0
  end
@d error(#)==begin
  history := error_given;
  show('Error: ',#)
@.Error:@>
end
@d warning(#)==begin
  if history = spotless then history := warning_given;
  show('Warning: ',#)
@.Warning:@>
end

@<Constants...@>==
 a = -1;

@* Optional modes of output.
\.{DVItoLN03} will print different quantities of information based on some
options that the user must specify: the typeout can be confined to a
restricted subset of the pages by specifying the desired starting page and
the maximum number of pages.  Further options permit
the selection of a different position for the origin on the page, which
by default is set to the ``standard'' of one inch from the top and left
edges of the paper, and selection of ``portrait'' or ``landscape'' orientation
for the output on the printed page.  All options are selected through the
use of VAX/VMS command qualifiers, but procedure |dialog| could be adapted
to carry out an interactive dialogue with a user on systems without VMS'
excellent facilities for defining commands.

The starting page is specified by giving a sequence of 1 to 10 numbers or
asterisks separated by dots. For example, the specification `\.{1.*.-5}'
can be used to refer to a page output by \TeX\ when $\.{\\count0}=1$
and $\.{\\count2}=-5$. (Recall that |bop| commands in a \.{DVI} file
are followed by ten `count' values.) An asterisk matches any number,
so the `\.*' in `\.{1.*.-5}' means that \.{\\count1} is ignored when
specifying the first page. If several pages match the given specification,
\.{DVItoLN03} will begin with the earliest such page in the file. The
default specification `\.*' (which matches all pages) therefore denotes
the page at the beginning of the file.

This version of \.{DVItoLN03} uses the VMS Command Language Interpreter utility
routines to determine whether command line qualifiers are present, and to
fetch their values, which are then stored in the following variables.
@^Command Language Interpreter@>
@^system dependencies@>
The default options that hold unless over-ridden by command line qualifiers
are:

\yskip\hang$\bullet$ \.{/starting\_page}=\.{*}
\yskip\hang$\bullet$ \.{/number\_of\_pages}=\.{1000000}
\yskip\hang$\bullet$ \.{/top\_margin} = \.{300px}
\yskip\hang$\bullet$ \.{/left\_margin}=\.{300px}
\yskip\hang$\bullet$ \.{/orientation}=\.{portrait}

@<Glob...@>=
@!left_marg,@!top_marg : integer;
@!orientation,@!new_orient,@!cur_orient : page_orientation;
@!h_fuzz,@!v_fuzz:integer; {controls how pedantic program is}
@!page_len,@!page_wid,@!y_min,@!x_min : integer;
@!max_pages:integer; {at most this many |bop..eop| pages will be printed}
@!resolution:real; {pixels per inch}
@!new_mag:integer; {if positive, overrides the postamble's magnification}

@ This type allows us to keep track of whether characters are currently
being imaged in landscape or portrait orientation.

@<Types...@>=
@!page_orientation = (@!portrait,@!landscape);

@ The starting page specification is recorded in two global arrays called
|start_count| and |start_there|. For example, `\.{1.*.-5}' is represented
by |start_there[0]=true|, |start_count[0]=1|, |start_there[1]=false|,
|start_there[2]=true|, |start_count[2]=-5|.
We also set |start_vals=2|, to indicate that count 2 was the last one
mentioned. The other values of |start_count| and |start_there| are not
important, in this example.

@<Glob...@>=
@!start_count:array[0..9] of integer; {count values to select starting page}
@!start_there:array[0..9] of boolean; {is the |start_count| value relevant?}
@!start_vals:0..9; {the last count considered significant}
@!count:array[0..9] of integer; {the count values on the current page}

@ @<Set init...@>=
max_pages:=1000000; start_vals:=0; start_there[0]:=false;
top_marg:=300; left_marg:=300; orientation:=portrait;

@ Here is a simple subroutine that tests if the current page might be the
starting page.

@p function start_match:boolean; {does |count| match the starting spec?}
var k:0..9;  {loop index}
@!match:boolean; {does everything match so far?}
begin match:=true;
for k:=0 to start_vals do
  if start_there[k]and(start_count[k]<>count[k]) then match:=false;
start_match:=match;
end;

@ The global variable |buf_ptr| is used while scanning each line of input;
it points to the first unread character in |buffer|, into which the command
line interpreter places strings by interaction with the CLI interface
procedures.

@<Glob...@>=
@!buffer: varying[terminal_line_length] of char;
@!buf_ptr:0..terminal_line_length; {the number of characters read}

@ The compiler needs to know about the ``foreign'' routines used for access to
the Command Line Interpreter. VAX-\PASCAL\ permits the ``declaration'' of
external procedures, where the declaration consists of the procedure heading
alone, followed by the reserved word |extern|, in a manner analogous to
standard \PASCAL's |forward| directive.
@^system dependencies@>

We also require the following special definitions, types, variables and
procedures to be able to get information from the command line interpreter.
The macro |VAX_string_or_empty| is used so as not to confuse W{\mc EAVE}'s
formatting of these foreign procedure interfaces.


@d VAX_volatile==@=[volatile]@>
@d VAX_immed==@=%immed @>
@d VAX_external==@=[external]@>
@d VAX_descr==@=%descr @>
@d VAX_ref==@=%ref @>
@d VAX_cli_present==@= cli$present@>
@d VAX_cli_get_value==@= cli$get_value@>
@d VAX_cli_negated==@"000381F8
@d VAX_lib_find_file==@= lib$find_file@>
@#
@f VAX_volatile==do_nothing
@f VAX_descr==var
@f VAX_ref==var
@f VAX_immed==integer
@f VAX_external==var
@#
@d VAX_string_or_empty(#)==VAX_volatile varying[@!#] of char := VAX_immed 0

@f extern==forward

@<Procedures for init...@>=
VAX_external @+function VAX_cli_present(
  VAX_descr @!entity:VAX_string_or_empty(s1_len)) : integer; extern;@#@t\2@>

VAX_external @+function VAX_cli_get_value(
  VAX_descr @!entity:VAX_string_or_empty(s1_len);
  VAX_descr @!returns:VAX_string_or_empty(s2_len);
  VAX_ref @!retlen:VAX_volatile sixteen_bits := VAX_immed 0):integer;
  extern;@#@t\2@>

VAX_external @+function VAX_lib_find_file(
  VAX_descr @!part_spec:VAX_volatile file_spec;
  VAX_descr @!result_spec:VAX_volatile file_spec;
  VAX_ref @!context:VAX_volatile integer;
  VAX_descr @!def_spec:VAX_volatile file_spec := VAX_immed 0;
  VAX_descr @!related_spec:VAX_volatile file_spec := VAX_immed 0;
  VAX_ref @!stv_addr:VAX_volatile integer := VAX_immed 0;
  VAX_ref @!user_flags:VAX_volatile integer := VAX_immed 0): integer;
  extern;@#@t\2@>

@ The following function |get_value| inputs the string corresponding to a
command-line qualifier and converts it to its numerical value. Calls of the
|VAX_cli_get_value(s,a)| return to the buffer parameter |a| the value placed
by the user on the command qualifier whose name is given by the string |s|;
being a system service type function, it also reports its success (or failure)
as its result.  The other system service used is |VAX_cli_present(s)| which
returns an odd (success) status indication if the qualifier whose name is
given by the string |s| \&{is} present in the command line.

One VAX-\PASCAL\ extension permits string variables to be used in place of
files for reading and writing values.  The |VAX_string_read(b,i,...)|
procedure reads values into variables |i,...| from the characters stored in
the string variable |b|.  The |VAX_string_status| function returns a value in
a manner entirely analogous to VAX-\PASCAL's external I/O |status| function,
but relating to the success of the previous string read.

@d VAX_string_read==@= readv@>
@d VAX_string_status==@= statusv@>

@p function get_value(qualifier: varying [$u1] of char):integer;
  var @!i,@!stat,@!len : integer; {local workspace}
    buffer: varying [terminal_line_length] of char; {buffer for qualifier
    string}
begin
  if odd(VAX_cli_present(qualifier)) then
  begin stat:=VAX_cli_get_value(qualifier,buffer); buffer:=buffer+' ';
  {append a space to ensure end can be found}
    VAX_string_read(buffer,i,VAX_continue);
    if VAX_string_status > 0 then
      abort('Bad /',qualifier,' value! (=',buffer,')')
@:fatal error Bad qualifier}{\quad \.{Bad /}\meta{qualifier} \.{value}@>
    else
      get_value:=i
  end else get_value:=-1
end;

@ Here is a routine that scans a (possibly signed) integer and computes
the decimal value. If no decimal integer starts at |buf_ptr|, the
value 0 is returned. The integer should be less than $2^{31}$ in
absolute value.

@p function get_integer:integer;
var x:integer; {accumulates the value}
@!negative:boolean; {should the value be negated?}
begin if buffer[buf_ptr]='-' then
  begin negative:=true; incr(buf_ptr);
  end
else negative:=false;
x:=0;
while (buffer[buf_ptr]>='0')and(buffer[buf_ptr]<='9') do
  begin x:=10*x+xord[buffer[buf_ptr]]-"0"; incr(buf_ptr);
  end;
if negative then get_integer:=-x @+ else get_integer:=x;
end;

@ One feature of the new DEClaser~2200 (otherwise known as the LN06) is a
capability of selecting from which paper tray a sheet shall be fed; the
printer has two separate trays, which can be loaded with different sizes or
colours of paper: a popular use is to have letterhead paper loaded in one
tray, and plain paper for continuation sheets in the other.  Both on this
printer, and on the DEClaser~2100 (LN05), any of five different types of
paper tray can be inserted into (respectively, either, or the only) tray
slot.  Paper cassettes are available to hold three different American sizes
of paper (letter, legal or executive), the ISO standard A4 size used in
Europe, or a stack of up to 15 envelopes.

The DEClaser~2200 can be fitted with a separate envelope feeder (in addition
to the two paper trays), which can hold up to 40~envelopes.  This envelope
feeder is supported by the program, but we don't (yet!) support one
further option of the LN06 which is a large capacity input tray that can
hold 1000 sheets.

In addition, both the 2100 and 2200 models can await manual feeding of
sheets.  Selection of paper source is not supported on the LN03, of course.

\noindent Here are the values that may be listed with the \.{/feed\_tray}
qualifier:
\yskip\hang\.{ALL=}\meta{tray type} All sheets will be fed from the
specified tray.
\yskip\hang\.{FIRST=}\meta{tray type} Feed the first sheet from the
specified tray; subsequent sheets will be fed from the \.{DEFAULT\_TRAY},
unless the next qualifier is used:
\yskip\hang\.{REST=}\meta{tray type} Feed the second and subsequent sheets
from the specified tray; the first sheet will be fed from the
\.{DEFAULT\_TRAY}, unless the \.{FIRST} tray has also been specified.

\noindent\meta{Tray types} can take the following values:
\yskip\hang\.{DEFAULT\_TRAY} Use whichever tray is set as the default by the
printer's initialization sequence.
\yskip\hang\.{TOP\_TRAY} (This is the only tray on the DEClaser~2100.)
\yskip\hang\.{BOTTOM\_TRAY}
\yskip\hang\.{ENVELOPE\_TRAY} Feed envelopes, from the large-capacity
envelope tray option on the DEClaser~2200.
\yskip\hang\.{MANUAL\_FEED} Wait for the operator to insert each sheet
manually.

\noindent The following values look as though they could be defined as a
          \PASCAL\ enumeration type, but we need to ensure a particular
          mapping to ordinal values, so we engage in a Knuthian trick here.
@^Knuth, D.~E.@>
@:TeX the Program}{{\sl\TeX\ the Program}@>
The values correspond to the $P_s$ parameter of the |DECASTC| (Automatic
Sheet-feeder Tray Control) control sequence.

@d default_tray=0
@d top_tray=1
@d bottom_tray=2
@d envelope_feeder=3
@d manual_feed=99

@<Types...@>=
@!tray_type=default_tray..manual_feed;

@ Here is a procedure which converts the \.{/feed\_tray} qualifier to the
appropriate numeric value.

@p procedure @!read_tray(option:file_spec; var result:tray_type);
begin
  result:=default_tray;
  if odd(VAX_cli_present(option+'.TOP_TRAY')) then
    result:=top_tray
  else if odd(VAX_cli_present(option+'.BOTTOM_TRAY')) then
    result:=bottom_tray
  else if odd(VAX_cli_present(option+'.ENVELOPE_TRAY')) then
    result:=envelope_feeder
  else if odd(VAX_cli_present(option+'.MANUAL_FEED')) then
    result:=manual_feed;
  if device_type=ln03 then result:=default_tray
end;
@ The selected options are put into global variables by the |dialog|
procedure, which is called just as \.{DVItoLN03} begins.  As already
explained, the |term_out| file is opened during the initialization phase, with
\.{carriage\_control}=\.{none}.  Having announced the program's name, the
command line interpreter library procedures are invoked to retrieve the
qualifiers and file name from the command line.

@p procedure dialog;
var k:integer; {loop variable}
begin rewrite(term_out); {prepare the terminal for output}
write_ln(term_out,banner,crlf);@/
@<Determine the desired |start_count| values@>;
@<Determine the desired |max_pages|@>;
@<Determine the desired |resolution|@>;
@<Determine the desired |new_mag|@>;
@<Determine the desired |left_marg| and |top_marg|@>;
@<Determine the desired |orientation|@>;
@<Determine the desired ``fuzzes''@>;
@<Determine the desired |print_mode|@>;
@<Determine the desired \\{feed\_trays}@>;
@<Determine the desired |duplex_by_page_numbers|@>;
@<Print all the selected options@>;
end;


@ @<Determine the desired |start...@>=
start_vals:=0; start_there[0]:=false;
if odd(VAX_cli_present('STARTING_PAGE')) then
  begin k:=VAX_cli_get_value('STARTING_PAGE',buffer); buffer:=buffer+' ';
  if (buffer[1]<>' ') then begin
    buf_ptr:= 1; k:=0;
    if buffer[1]<>' ' then
      repeat if buffer[buf_ptr]='*' then
        begin start_there[k]:=false; incr(buf_ptr);
        end
      else begin start_there[k]:=true; start_count[k]:=get_integer;
        end;
      if (k<9)and(buffer[buf_ptr]='.') then
        begin incr(k); incr(buf_ptr);
        end
      else start_vals:=k { anything ill-formed we just stop }
      until start_vals=k
   end
end

@ @<Determine the desired |max_pages|@>=
max_pages:=1000000;
max_pages:=get_value('NUMBER_OF_PAGES');
if max_pages<=0 then max_pages:=1000000;

@ @<Determine the desired |resolution|@>=
resolution:=300.0; { this is the LN03's resolution }

@ @<Determine the desired |new_mag|@>=
new_mag:=0; { no provision for magnified LN03 output yet }

@ The value associated with the \.{/left\UL margin} and \.{/top\UL margin}
qualifiers consists of a number and a specifier for a \TeX\ \meta{physical
unit}.  The values provided are translated into pixels; this uses the same
mechanism as is used for interpreting similar dimensions in \.{\BS special}
commands, so the qualifier is copied into the |spec_par| buffer used for
that interpretation.  The characters have to be converted from external
representation into |ASCII_code| for this.

@<Determine the desired |left_marg|...@>=
if odd(VAX_cli_present('LEFT_MARGIN')) then
begin
  k:=VAX_cli_get_value('LEFT_MARGIN',buffer); buffer:=buffer+' ';
  @<Copy qualifier into |spec_par|@>;
  if rd_dimension(left_marg) then
  begin
    print_ln(' --- assuming pixel units');
    write_ln(' --- assuming pixel units',crlf);
    term_offset:=0
  end
@.assuming pixel units@>
end else left_marg := -1;
if left_marg<0 then left_marg:=300;

if odd(VAX_cli_present('TOP_MARGIN')) then
begin
  k:=VAX_cli_get_value('TOP_MARGIN',buffer); buffer:=buffer+' ';
  @<Copy qualifier into |spec_par|@>;
  if rd_dimension(top_marg) then
  begin
    print_ln(' --- assuming pixel units');
    write_ln(' --- assuming pixel units',crlf);
    term_offset:=0
  end
@.assuming pixel units@>
end else top_marg := -1;
if top_marg<0 then top_marg:=300;

@ Here's how we copy the |char| values from the |buffer| (whence they have
been read from the command-line qualifier) into the |spec_par| buffer,
converting to |ASCII_code| in the process.

@<Copy qualifier into |spec_par|@>=
  buf_ptr:=1; spec_len:=1;
  while buf_ptr<=buffer.length do
  begin
    spec_par[spec_len]:=xord[buffer[buf_ptr]];
    incr(spec_len); incr(buf_ptr)
  end;
  spec_ptr:=1

@ When the paper orientation is selected, we may define the |page_len| and
|page_wid| in terms of the physical limits of the paper; these sizes have
been defined at the start of this \.{web}.

@<Determine the desired |orientation|@>=
if odd(VAX_cli_present('ORIENTATION')) then
begin
  if odd(VAX_cli_present('PORTRAIT')) then orientation:=portrait else
  if odd(VAX_cli_present('LANDSCAPE')) then orientation:=landscape else
  warning('/ORIENTATION must be "PORTRAIT" or "LANDSCAPE"; assuming "PORTRAIT"')
@:Warning: orientation must be}{\quad \.{/ORIENTATION must be ...}@>
end;
if orientation=landscape then
begin page_len:=paper_wid+page_x_min; page_wid:=paper_ht+y_land_min;
  y_min:=page_x_min; x_min:=y_land_min end else
begin page_len:=paper_ht+y_port_min; page_wid:=paper_wid+page_x_min;
  y_min:=y_port_min; x_min:=page_x_min end;

@ The value associated with the \.{/hfuzz} and \.{/vfuzz}
qualifiers consists of a number and a specifier for a \TeX\ \meta{physical
unit}.  The values provided are translated into scaled points, entirely
analogously with the translation of the value on the \.{/top\_margin}
qualifier into pixels.

These ``fuzz'' values are used to control how pedantic the program is when
it discovers characters which extend beyond the right-hand margin as
reported (in the \.{DVI} file) by \TeX.  The default values for these
qualifiers is \.{100sp}, which implies that ``errors'' smaller than this
(it's a very small quantity, approximately equal to the wavelength of
visible light) will be ignored.  Normally \TeX\ wouldn't dream of exceeding
the margin (well, not without issuing an \.{overfull \\hbox} warning), but
with some macro packages it isn't so fussy. For example, the output of W{\sc
EAVE}, when formatting the \PASCAL\ part of a program, can extend beyond the
margin by up to \.{10pt}.  By selecting a larger value of ``fuzz'',
spurious warning messages from DVItoLN03 can be suppressed.

@<Determine the desired ``fuzzes''@>=
if odd(VAX_cli_present('HFUZZ')) then
begin
  k:=VAX_cli_get_value('HFUZZ',buffer); buffer:=buffer+' ';
  @<Copy qualifier into |spec_par|@>;
  if rd_scaled_pt(h_fuzz) then
  begin
    print_ln(' --- assuming scaled points');
@.assuming scaled points@>
    write_ln(' --- assuming scaled points',crlf);
    term_offset:=0
  end
end else h_fuzz := 0;
decr(h_fuzz); {when used, we watch for |h-max_h| exceeding this value}
if h_fuzz<0 then h_fuzz:=99; {corresponds to default of \.{100sp}}

if odd(VAX_cli_present('VFUZZ')) then
begin
  k:=VAX_cli_get_value('VFUZZ',buffer); buffer:=buffer+' ';
  @<Copy qualifier into |spec_par|@>;
  if rd_scaled_pt(v_fuzz) then
  begin
    print_ln(' --- assuming scaled points');
@.assuming scaled points@>
    write_ln(' --- assuming scaled points',crlf);
    term_offset:=0
  end
end else v_fuzz := 0;
decr(v_fuzz);
if v_fuzz<0 then v_fuzz:=99

@ The program can support the traditional LN03 (and LN03-plus) printers (but
not the LN03R ScriptPrinter, for which a DVI to PostScript program
@^PostScript@>
is required), the LN05 (DEClaser~2100) and the LN06 (DEClaser~2200).

This variable reminds of which mode is in use:

@<Glob...@>=
@!device_type:(@!ln03,@!ln05,@!ln06);

@ This section determines which value was used with the \.{/DEVICE\_TYPE}
qualifier.  It is an error (something's gone wrong with the command
definition file) if this qualifier doesn't have a value.

@<Determine the device type@>=
device_type:=ln03;
if not odd(VAX_cli_present('DEVICE_TYPE')) then
  abort('No device type specified')
@:fatal error No device type specified}{\quad\.{No device type specified}@>
else
  if odd(VAX_cli_present('DEVICE_TYPE.LN03')) then
    device_type:=ln03
  else
  if odd(VAX_cli_present('DEVICE_TYPE.LN05')) then
    device_type:=ln05
  else
  if odd(VAX_cli_present('DEVICE_TYPE.LN06')) then
    device_type:=ln06

@ The new DEClaser printers (models 2100 and 2200, otherwise known as the
LN05 and LN06) are capable of a number of extra ``tricks'', compared with
the original LN03; in particular, the DEClaser~2200 can print on both sides
of the paper (``duplex'' printing).  The \.{/print\_mode} qualifier of the
command line interface allows us to select a number of different modes of
operation.

\noindent The possible modes are as follows:
\yskip\hang\.{SIMPLEX} Print on one side of paper only; this is the default,
and the only method compatible with the DEClaser~2100 (LN05) and the
original LN03 (but see below).
\yskip\hang\.{DUPLEX} Print on both sides of the paper; which page gets
printed on the reverse of such a sheet is further controlled by the
\.{/duplex\_by\_page\_numbers} qualifier.
\yskip\hang\.{MASTER} Can be used in conjunction with the \.{DUPLEX} value
to produce a master set of sheets for photocopying or other production
process.  The program behaves as if the printer were operating in duplex
mode, but actually prints on only one side of the paper, and ``prints'' a
blank page if it would have left the \\{verso} page blank.  This option can
be used with the DEClaser~2100 (LN05), and with the traditional LN03, even
though those printers don't support genuine duplexing.
\yskip\hang\.{TUMBLED} Can be used in conjunction with genuine duplex
printing only (and thus is restricted to the DEClaser~2200).  Causes
\\{verso} (left-hand) pages to be printed upside-down, therefore making the
final document suitable for binding along the short edge (long edge in
landscape mode) to make a flip-chart style document.
\yskip\hang\.{NORMAL} The opposite of \.{TUMBLED}.  This is the default.

\noindent Once again, we define an `enumeration' type with known values:

@d default_print_mode=0
@d true_simplex_normal=1
@d true_simplex_tumbled=2
@d true_duplex_normal=3
@d true_duplex_tumbled=4
@d duplex_master_normal=5
@d duplex_master_tumbled=6
@d ln03_master=7  {we have to handle this by software, since no printer support}

@<Glob...@>=
@!print_mode:default_print_mode..ln03_master;

@ Here's where we analyse the \.{/print\_mode} qualifier, and set
|print_mode| accordingly:

@<Determine the desired |print_mode|@>=
@<Determine the device type@>;
print_mode:=default_print_mode;
if odd(VAX_cli_present('PRINT_MODE')) then
  if odd(VAX_cli_present('PRINT_MODE.DUPLEX')) then
    if odd(VAX_cli_present('PRINT_MODE.MASTER')) then
      if odd(VAX_cli_present('PRINT_MODE.TUMBLED')) then
        print_mode:=duplex_master_tumbled
      else print_mode:=duplex_master_normal
    else
      if odd(VAX_cli_present('PRINT_MODE.TUMBLED')) then
        print_mode:=true_duplex_tumbled
      else print_mode:=true_duplex_normal
  else
    if odd(VAX_cli_present('PRINT_MODE.TUMBLED')) then
      print_mode:=true_simplex_tumbled
    else print_mode:=true_simplex_normal;
if device_type=ln03 then
  if print_mode>=duplex_master_normal then
    print_mode:=ln03_master
  else
    print_mode:=default_print_mode

@ Now we decide which tray shall be used for the first and subsequent sheets

@<Determine the desired \\{feed\_trays}@>=
if odd(VAX_cli_present('FEED_TRAY')) then
  if odd(VAX_cli_present('FEED_TRAY.ALL')) then
  begin
    read_tray('FEED_TRAY.ALL',first_tray);
    following_tray:=first_tray
  end
  else
  begin
    read_tray('FEED_TRAY.FIRST',first_tray);
    read_tray('FEED_TRAY.REST',following_tray)
  end

@ Here are the variables needed to save the print tray(s) selection; also
one which determines in which manner duplexing shall take place

@<Glob...@>=
@!first_tray,@!following_tray:tray_type;
@!duplex_by_page_numbers:boolean;

@ When we are printing on both sides of the paper, or simulating that
operation with the \.{MASTER} option on the \.{/PRINT\_MODE} qualifier, we
will usually elect to print \\{verso} pages when the page number (held in
|count[0]|) is even, and \\{recto} pages when it is odd (or zero).  If two
odd or two even pages appear in succession, the other side of the sheet will
be left blank (or a blank page ejected in \.{MASTER} mode).

This is the default mode of operation, when the \.{/DUPLEX\_BY\_PAGE\_NUMBERS}
qualifier is present; if this qualifier is negated, then the \\{verso} side
of the sheet will be covered with whatever page follows that printed on the
\\{recto} side, regardless of the page number; no blank pages appear, unless
explicitly generated by \TeX.

@<Determine the desired |duplex_by_page_numbers|@>=
duplex_by_page_numbers:=odd(VAX_cli_present('DUPLEX_BY_PAGE_NUMBERS'))

@ After the dialogue is over, we print the options so that the user
can see what \.{DVItoLN03} thought was specified.

These macros allow us to show which |print_mode| and feed tray have been
selected:

@d show_print_mode==begin
print('Print mode is ');
case print_mode of
  true_simplex_normal,true_duplex_normal,duplex_master_normal:
    print('normal ');
  true_simplex_tumbled,true_duplex_tumbled,duplex_master_tumbled:
    print('tumbled ')
endcases;
if print_mode=ln03_master then print('ln03 ');
if print_mode>=duplex_master_normal then print('master ');
if print_mode>=true_duplex_normal then
  print_ln('duplex')
else
  print_ln('simplex')
end
@#
@d show_tray(#)==case # of
  default_tray: print_ln('fed from the default tray');
  top_tray: print_ln('fed from the top tray');
  bottom_tray: print_ln('fed from the bottom tray');
  envelope_feeder: print_ln('taken from the envelope feeder');
  manual_feed: print_ln('manually fed')
endcases

@<Print all the selected options@>=
print_ln('Options selected:');
@.Options selected@>
print('  Starting page = ');
for k:=0 to start_vals do
  begin if start_there[k] then print(start_count[k]:1)
  else print('*');
  if k<start_vals then print('.')
  else print_ln(' ');
  end;
print_ln('  Maximum number of pages = ',max_pages:1);
print_ln('  Printing starts ',top_marg:1,' pixels from the top of the paper, and');
print_ln('    ',left_marg:1,' pixels from the left-hand edge');
print_ln('  Orientation = ',orientation);
print_ln('  Resolution = ',resolution:12:8,' pixels per inch');
if new_mag>0 then print_ln('  New magnification factor = ',new_mag/1000:8:3);
if h_fuzz>99 then print_ln('  Horizontal overruns less than ',
                            (h_fuzz+1)/65536.0:1:2,'pt will be ignored');
if v_fuzz>99 then print_ln('  Depth overruns less than ',
                            (v_fuzz+1)/65536.0:1:2,'pt will be ignored');
if (first_tray<>default_tray) or (following_tray<>default_tray) then
  if first_tray<>following_tray then
  begin
    print('First sheet is '); show_tray(first_tray);
    print('Following sheets are '); show_tray(following_tray)
  end
  else
  begin
    print('Paper is '); show_tray(first_tray)
  end;
if print_mode<>default_print_mode then
  show_print_mode;
if print_mode>=true_duplex_normal then
begin
  print('Duplexing is ');
  if not duplex_by_page_numbers then print('not ');
  print_ln('controlled by page numbers')
end

@* Defining fonts.
\.{DVItoLN03} reads the postamble first and loads
all of the fonts defined there; then it processes the pages. Therefore
a \\{fnt\_def} command should match a previous definition if and only
if the \\{fnt\_def} being processed is not in the postamble.


Since the ``wanted'' pages are read twice, {\it viz.} whilst gathering the
font usage statistics and to perform the actual type setting, font
definitions in the \.{DVI} file will actually be met {\bf three} times. The
original logic of \.{DVItype} using the global variable |in_postamble|
covers the first two occasions during the  initial scan and the reading of
the postamble.  The variable |substitution_pass| prevents us from getting a
``font already defined'' message when each definition is met for the third
time.

@<Glob...@>=
@!substitution_pass:boolean; {are we performing the actual type setting?}
@!in_postamble:boolean; {are we reading the postamble?}

@ @<Set init...@>=
substitution_pass:=false;
in_postamble:=false;

@ Under the VAX/VMS operating system, no distinction is made between fonts
names \.{cmr10} and \.{CMR10}, for example.  However, if \TeX\ encounters
a font name not written in the same case as those of the preloaded
format in use, it will think it is a distinct font (and under some other
operating systems, of course, the case of the name could be significant).
The following function allows us to fold upper-case letters in a font name
to lower-case, and thus avoid such infelicitous duplication.

@p function lower(@!ch:ASCII_code): ASCII_code;
begin
  if (ch>="A") and (ch<="Z") then
    lower:=ch+"a"-"A"
  else
    lower:=ch
end;

@ The following subroutine does the necessary things when a \\{fnt\_def}
command is being processed.

@p procedure define_font(@!e:integer); {|e| is an external font number}
var @!f:0..max_fonts; {local index into font data structures}
@!p:integer; {length of the area/directory spec}
@!newly_defined: boolean; {first time font has been defined}
@!n:integer; {length of the font name proper}
@!c,@!q,@!d:integer; {check sum, scaled size, and design size}
@!r:0..name_length; {index into |cur_name|}
@!j,@!k:0..name_size; {indices into |names|}
@!mismatch:boolean; {do names disagree?}
begin if nf=max_fonts then capacity_exceeded('too many fonts (max fonts=',
                                             max_fonts:1,')');
@:capacity exceeded too many fonts}{\quad \.{too many fonts}@>
if e > highest_font then highest_font := e;
font_num[nf]:=e; f:=0; font_map[nf]:=nf;
while font_num[f]<>e do incr(f);
@<Read the font parameters into position for font |nf|, and
  print the font name@>;
if in_postamble then
  begin if (f<nf) and not substitution_pass then
    warning('---this font was already defined!');
@:Warning: this font was already defined}{\quad \.{this font was already defined}@>
  end
else  begin if f=nf then warning('---this font wasn''t loaded before!');
@:Warning: this font wasn't loaded before}{\quad \.{this font wasn't loaded before}@>
  end;
newly_defined:=f=nf;@/
@<Avoid reloading the font metrics, if identical font already present@>;
if f=nf then @<Load the new font, unless there are problems@>
else
begin
  @<Check that the current font definition matches the old one@>;
  if newly_defined and (font_map[nf]<nf) then
  begin
    print('---is identical to font ',font_num[f]:1);
@.is identical to font ...@>
    incr(nf) {the ``new'' font is officially present, but remapped}
  end
end;
end;

@ When we reach the point at which it might be found necessary to read the
\TeX\ font metrics from the \.{TFM} file, we can save a little effort if
we've already loaded a font with identical name and at the same scaled size.
In fact, this is essential with such virtual fonts as \.{recurse}, because
otherwise we might go on loading the same \.{TFM} file \\{ad infinitum}!
Variable |mismatch| is misused in the |while| loop such that the loop will
exit if the name of font |f| is identical to that of font |nf|.  If the
names differ, the loop continues until |f=nf|.

@<Avoid reloading the font metrics, if identical font already present@>=
f:=font_map[f]; {it might already map elsewhere!}
if f=nf then {it's got to \&{be} a new font, of course!}
begin
  f:=0; mismatch:=true;
  while (f<nf) and mismatch do
  begin
    if font_scaled_size[f]=q then {don't try name unless same size}
      @<Compare two font names, and set |mismatch| accordingly@>;
    if mismatch then incr(f)
  end; {|while f|}
  if f<nf then {the font already exists, under a different identity}
    font_map[nf]:=f;
end

@ If the \.{DVI} file has previously defined this font, we check that the
parameters of this |fnt_def| command match those previously read: there's
something amiss if they differ (and the user is supposed to determine what
by using \.{DVItype}).

@<Check that the current...@>=
begin if font_check_sum[f]<>c then
  warning('---checksum doesn''t match previous definition!');
@:Warning: checksum doesn't match}{\quad\.{checksum doesn't match}@>
if font_scaled_size[f]<>q then
  warning('---scaled size doesn''t match previous definition!');
@:Warning: scaled size doesn't match}{\quad\.{scaled size doesn't match}@>
if font_design_size[f]<>d then
  warning('---design size doesn''t match previous definition!');
@:Warning: design size doesn't match}{\quad\.{design size doesn't match}@>
@<Compare two font names, and set |mismatch| accordingly@>;
if mismatch then warning('---font name doesn''t match previous definition!');
@:Warning: font name doesn't match}{\quad\.{font name doesn't match}@>
end

@ This little chunk of code compares the font names stored in
|names[font_name[f]..font_name[f+1]-1]| and
|names[font_name[nf]..font_name[nf+1]-1]|; if any character differs, or the
lengths of the names differ, then |mismatch| will be set.

@<Compare two font names, and set |mismatch| accordingly@>=
begin
  j:=font_name[f]; k:=font_name[nf]; mismatch:=false;
  while j<font_name[f+1] do
  begin
    if names[j]<>names[k] then mismatch:=true;
    incr(j); incr(k);
  end; {|while j|}
  if k<>font_name[nf+1] then mismatch:=true
end
@ @<Read the font parameters into position for font |nf|...@>=
c:=signed_quad; font_check_sum[nf]:=c;@/
q:=signed_quad; font_scaled_size[nf]:=q;@/
d:=signed_quad; font_design_size[nf]:=d;@/
p:=get_byte; n:=get_byte;
if font_name[nf]+n+p>name_size then
  capacity_exceeded('too many names (name size=',name_size:1,')');
@:capacity exceeded too many names}{\quad\.{too many names}@>
font_name[nf+1]:=font_name[nf]+n+p;
if not substitution_pass then
    print('Font ',e:1,': ');
if n+p=0 then print('null font name!')
@.null font name@>
else for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=lower(get_byte);
incr(nf); if not substitution_pass then print_font(nf-1); decr(nf)

@ @<Load the new font, unless there are problems@>=
begin @<Move font name into the |cur_name| string@>;
open_tfm_file;
if (q<=0)or(q>=@'1000000000) then
    abort('Font not loaded, bad scale (',q:1,')!')
@:fatal error Font not loaded bad scale}{\qquad\.{bad scale}@>
  else if (d<=0)or(d>=@'1000000000) then
    abort('Font not loaded, bad design size (',d:1,')!')
@:fatal error Font not loaded bad design size}{\qquad\.{bad design size}@>
  else if in_TFM(q) then @<Finish loading the new font info@>;
end

@ Strictly speaking, we should derive the width of a `thin space' from
|param[6]| in the |tfm_file|, but this program (in common with such paragons
of virtue as \.{DVItype}!) doesn't bother to read that far in the file; so
we assume that \.{1em} in the font is equivalent to the design size, and
knowing that an \.{em} in the Computer Modern fonts is \.{18u\#} wide, take
one-sixth of this to represent the `thin space'.

@<Finish loading...@>=
begin font_space[nf]:=q div 6; {this is a 3-unit ``thin space''}
if (c<>0)and(tfm_check_sum<>0)and(c<>tfm_check_sum) then
  warning('checksum doesn''t match; DVI=',c:1,' vs. TFM=',tfm_check_sum:1);
@:Warning: checksum doesn\'t match}{\quad\.{checksum doesn't match}@>
print('---loaded at size ',q:1,' DVI units');
d:=round((100.0*conv*q)/(true_conv*d));
if d<>100 then
  print(' (this font is magnified ',d:1,'%)');
@.this font is magnified@>
incr(nf); {now the new font is officially present}
font_space[nf]:=0; {for |out_space| and |out_vmove|}
end

@ The string |cur_name| is supposed to be set to the external name of the
\.{TFM} file for the current font. This usually means that we need to
prepend the name of the default directory, and
to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
to upper case, since |cur_name| is a \PASCAL\ string.
If |p=0|, i.e., if no font directory has been specified, \.{DVItoLN03}
is supposed to use the default font directory, which is a
system-dependent place where the standard fonts are kept; in the current
implementation, this will have been read into |tfm_directory| from the
command-line qualifier \.{/font\UL directory}.
@^system dependencies@>

@<Move font name into the |cur_name| string@>=
for k:=1 to name_length do cur_name[k]:=' ';
if p=0 then
  begin for k:=1 to tfm_directory.length do
    cur_name[k]:=tfm_directory.body[k];
  r:=tfm_directory.length;
  end
else r:=0;
for k:=font_name[nf] to font_name[nf+1]-1 do
  begin incr(r);
  if r+4>name_length then
    capacity_exceeded('font file name too long (max length=',name_length:1,')');
@:capacity exceeded font file name}{\quad\.{font file name too long}@>
  if (names[k]>="a")and(names[k]<="z") then
      cur_name[r]:=xchr[names[k]-@'40]
  else cur_name[r]:=xchr[names[k]];
  end;
cur_name[r+1]:='.'; cur_name[r+2]:='T'; cur_name[r+3]:='F'; cur_name[r+4]:='M'

@* Accumulation of page.  We save a page before outputting it, to minimize the
complexity of the page for the LN03.

The page is stored as a series of linked lists of ``segments'', with
(potentially) one such list being stored for each pixel position in the
vertical direction.

The individual segments are a structure, containing the $x$-coordinates of the
start and finish of the text.  We also record the current font in which the
text is to be set, and a pointer to the next segment in the list.

We therefore declare here the structure to hold the segment, and an array of
pointers, one for each vertical pixel position.  We also provide a further two
segment pointers, one for the ``current'' segment, and another pointing to a
list of allocated, but otherwise unused, segments.  Thus it is only necessary
to call for storage allocation when the free list is exhausted.

If a glyph proved to have too large a bitmap for it to have been downloaded
as a character into the LN03, a segment was appended which references that
glyph alone; this is necessary to ensure that the bitmaps are only output
once the |eop| has been met, to accommodate changes of orientation brought
about by \.{\\special} commands.

@d max_text=max_print   {Largest text segment held in a record}

@<Types in the...@>=
  @!string  = varying [10] of char;
      {to hold character representations of integers}
  @!segment = packed record
          @!next : ^segment; {pointer to next segment, or |nil|}
          @!xs,@!xe : integer;  {$x$-coords of start and finish of text}
          @!font : integer; {LN03 downloaded font \#; external font for bitmaps}
          @!ch   : eight_bits; {character from LN03 internal font}
          @!seg_type : (@!glyph_string,@!bitmap,@!sixel_dump);
          @!direction : page_orientation; {|landscape| or |portrait|}
          @!text : varying [max_text] of char; {sequence for output to printer}
        end;

@ There is one |seg_list| pointer for each possible horizontal row of pixels
on the paper; |cur_seg| points to the current segment, whilst |free_list_seg|
maintains a list of free segments.  Any characters which would appear outside
the bounds of the paper are accumulated in segments of the |lost_seg| list.

@<Globals in the ...@>=
  @!seg_list : array [y_land_min..paper_ht+y_port_min] of ^segment;
  @!cur_seg, @!free_seg_list : ^segment;
  @!lost_seg : ^segment;        {For segments set off the paper}
  @!seg_max : integer; {could be used for statistics}

@ @<Set initial...@>=
  free_seg_list:=nil; cur_seg:=nil; lost_seg:=nil;
  for k:=y_land_min to paper_ht+y_port_min do seg_list[k]:=nil;
  seg_max:=0;
  top_of_page:=true;

@ The procedure |new_segment| allocates storage for a new segment.  The
program maintains a |free_seg_list| to which processed segments are returned;
however, if no free segments remain on this list (as will obtain on first use
of the program), the \PASCAL\ |new| function is used to allocate an
appropriate area.

After the segment has been allocated, as many fields as possible are
completed: (1) the |next| field points to the existing |seg_list| to which
this new segment now belongs; (2) the |font| field records the font currently
selected; (3) the |seg_list| is made to point to this new segment.  If the
text is known to be invalid (for the case in which it has been discovered that
the text will not fit on the physical paper), then the new segment is added
instead to the |lost_seg| list, and the |font| field is used to record the
$y$-position at which \TeX\ had tried to locate the text.

@p  procedure new_segment(@!valid,@!offset:boolean;
                          @!font_code,@!ch_code:integer);
  begin
    @<Find or create a new |cur_seg|@> ;
    @<Set fields of |cur_seg|@>
  end; {|new_segment|}

@ If the |free_seg_list| is empty, either we've never allocated any segments
before, or we've used them all.  We therefore use the standard \PASCAL\
allocator function, |new|.

Alternatively, we just detach the first segment from the |free_seg_list|.

@<Find or create a new |cur_seg|@>=
if free_seg_list = nil then
begin new(cur_seg);
  incr(seg_max)
end else
begin
  cur_seg:=free_seg_list;
  free_seg_list:=cur_seg^.next
end

@ If the character being set is correctly within the imaging area of the
paper, we add the new segment to the segment list associated with its vertical
position on the page, and record the |ln_font| in which the character shall be
output.

Alternatively, we add the segment to the list of |lost_seg|s, and use the
|font| field to record the vertical |y_pos| at which \TeX\ \\{thought} it
was imaging the text.

References to actual paper positions are always offset by the setting of
|left_marg| and |top_marg|.

@d x_pos==hh+left_marg          {Actual paper coordinates}
@d y_pos==vv+top_marg

@<Set fields of |cur_seg|@>=
with cur_seg^ do
  if valid then
  begin
    ch:=ch_code;
    direction:=new_orient;
    if offset then {oversized glyphs and sixel graphics inclusions}
    begin
      next:=seg_list[y_pos+bitmap_offset]; {Goes on `lower' list}
      seg_type:=bitmap; {this is corrected externally if necessary}
      font:=cur_font; {record index into fonts for later file opening}
      seg_list[y_pos+bitmap_offset]:=cur_seg    {Put at front of the list}
    end
    else
    begin
      next:=seg_list[y_pos];
      seg_type:=glyph_string;
      font:=font_code; {record number of a \\{downloaded} font}
      seg_list[y_pos]:=cur_seg  {Put at front of the list}
    end
  end else
  begin next:=lost_seg; {Don't put it on paper}
    font:=y_pos;                {Remember where \TeX\ wanted to put it}
    lost_seg:=cur_seg
  end


@ Procedure |finish_seg| is used to discard segments which record text which
was set off the paper, after making appropriate error indications in the log
(\.{TYP}) file.  Discarded segments are returned to the |free_seg_list|.

The procedure is also called when a glyph is met that cannot become a member
of that held in |cur_seg|; this just results in |cur_seg:=nil|.  The segment
is already attached to some part of |seg_list| array, so nothing further is
required, and nothing gets lost!

@p  procedure finish_seg;
  begin
    while (cur_seg<>nil) and (cur_seg=lost_seg) do
    begin                       {We've got something set off the paper}
      with cur_seg^ do
      begin
        warning('Characters ''',text,''' set off paper ');
@:Warning: Characters set off paper}{\quad\.{Characters ... set off paper}@>
        write_ln(term_out,'(see log file)',crlf);
        print_ln('( x = ',xs:1,'..',xe:1,', y = ',font:1,' ) - ignored');
        lost_seg:=next; next:=free_seg_list; free_seg_list:=cur_seg;
        cur_seg:=lost_seg
      end;
    end;
    cur_seg:=nil                {Forget that we've got a current segment}
  end;

@ The function |str_int| yields a minimum length ASCII representation for its
integer parameter |num|.  It does this by invoking the VAX-\PASCAL\ specific
function |dec(n,f,d)|, which yields an |f| character-long string containing
the decimal representation of the integer |n| occupying |d| digits at the
right-hand end of the string; if necessary leading `\.0' characters are
inserted to ensure that |d| digits are printed. (The leading |f-d| characters
of the string would be filled with the space character `\.\SP', but we
ensure that |f| and |d| are both just wide enough to avoid such a problem!)

@d VAX_decimal==@=dec@>

@p  function str_int(num : integer) : string;
  var field,digits : integer;
  begin if num = 0 then digits:=1 else
    digits:=trunc(ln(abs(num))/ln(10))+1;
    {$\bigl\lceil\log_{10}\vert\hbox{\\{num}}\vert\bigr\rceil$ is number of digits
    required}
    field:=digits;
    if num < 0 then incr(field);
    str_int:=VAX_decimal(num,field,digits)
  end;

@ We define here some non-printing control characters, which will later be
required for control and escape sequences.

@d LF==chr(@"0A)
@d FF==chr(@"0C)
@d esc==chr(@"1B)       {The ASCII ESCape character}
@d csi==chr(@"9B)       {Control Sequence Introducer}   
@d dcs==chr(@"90)       {Device Control String}
@d st==chr(@"9C)        {String Terminator}

@ The following |move_forward| procedure includes within the text of a segment
the appropriate LN03 |HPR| (Horizontal Position Relative) control sequence to
instruct the printer to move the printing position to the right.  This
sequence consists of the control sequence introducer, |csi|, followed by the
motion expressed in pixels, followed by the sequence terminator `\.a'.

@p  procedure move_forward(dist:integer);
  begin
    cur_seg^.text:=cur_seg^.text + csi+str_int(dist)+'a';
  end;

@ Because of the restrictions of \PASCAL, we have to make a |forward|
declaration of procedure |do_page|.  The procedure is called (recursively)
to interpret  the |dvi| bytes stored from a character packet in a virtual
font.  It also implements the normal operations for interpreting the
contents of a page read from the \.{DVI} file.

@p function do_page(@!vf_start:integer):boolean; forward;

@ Procedure |ord_text| adds a single character, |ch|, taken from the
|cur_font|, to a segment preparatory to its being ``set'' on the paper.  The
procedure cannot be declared at this point in the \.{WEB} source because it
references a number of procedures that haven't yet been declared. However,
we'll arrange for it to appear in the correct part of the \PASCAL\ later on.

If the character is taken from a virtual font, and requires more than the
simple operation of setting a single character from a single font, then this
procedure calls |do_page| recursively to interpret the sequence of |dvi|
bytes that have been stored into |vf| from the \.{VF} file.

This macro is equivalent to |glyph_map(cur_font)(#)|, assuming that
|cur_base=glyph_base[cur_font]|.

@d cur_font_glyph(#)==glyphs[cur_base+#]

@<Declare the |ord_text| Procedure@>=
procedure ord_text(ch:integer);
    var @!good : boolean;
begin
  with cur_font_glyph(ch) do
  begin
    @<Check if glyph appears within printable area@>;
    @<Allocate segment to hold text string@>;
    if loaded=virtual then
    begin
      finish_seg;       {Whatever precedes this character to be treated
separately}
      if not do_page(seq_off) then
        abort('VF packet failed [',cur_font:1,',',ch:1,']!');
@:fatal error VF packet failed}{\quad\.{VF packet failed}@>
      finish_seg;       {Don't run into next character or record this one's
width twice}
    end
    else begin
      @<Ensure entered text does not overflow |cur_seg|@>;
      @<Set character onto page@>
    end
  end
end {|procedure ord_text|}

@ If the character would be set off the physical paper addressable by the LN03
printer, the segment belongs to the |lost_seg| list.

@<Check if glyph appears within printable area@>=
if (x_pos<x_min) or (x_pos>page_wid) or (y_pos<y_min) or (y_pos>page_len) then
begin
  good := false;
  if (cur_seg<>nil) then
  begin
    if (cur_seg<>lost_seg) then
      finish_seg {earlier glyphs OK, even though this is off paper}
    else
      if cur_seg^.font<>y_pos then finish_seg {these were off paper, but on
                                               a different row}
  end;
end else@/
begin   {The character fits on the paper}
  good := true;
  if cur_seg<>nil then
  begin
    if cur_seg=lost_seg then finish_seg {report earlier off-paper chars}
    else
      if cur_seg^.seg_type=bitmap then finish_seg; {previous segment contains
                                                    single oversized glyph}
    if loaded=no then finish_seg {this segment will be ditto}
  end
end

@ If there is no |cur_seg| to hold the text, one is allocated, and the start and
finish coordinates of the text therein recorded.  The |text| field is set to be
the null string.

@<Allocate segment to hold text string@>=
if cur_seg = nil then
begin new_segment(good,loaded=no,font_code,ch);
  cur_seg^.xs:=x_pos;
  cur_seg^.xe:=x_pos;
  cur_seg^.text:=''
end

@ Checks are made that this segment (1) would not produce too large a
contiguous sequence of characters (which might therefore lead to an overlong
output record); (2) that the character does not need to be positioned to the
\&{left} of those already in the segment; (3) that the LN03 font used for this
character is the same as that for the other characters of the segment.  If any
of these checks fail, we commence a new segment; these tests are bypassed if
we're setting a single oversized glyph.

If any additional rightward movement is needed, the appropriate escape sequence
is added to the |text| in the |cur_seg|.

@<Ensure entered text does not overflow |cur_seg|@>=
if cur_seg^.seg_type=glyph_string then
begin
  if (VAX_length(cur_seg^.text)>=max_text-8) or
      (x_pos<cur_seg^.xe) or
      (font_code<>cur_seg^.font) then
  begin finish_seg;
    @<Allocate segment...@> {tests enumerated above failed}
  end
  else
  if x_pos>cur_seg^.xe then             {We need to move rightward}
    move_forward(x_pos-cur_seg^.xe)    {Add character to segment}
end

@ Characters whose glyphs proved too large to download in an LN03 font are
saved up to be printed as a pixel dump; other glyphs are recorded in the
current segment for imaging through the downloaded fonts.

If the character being stored is only being recorded to support generation
of an error message,  it is translated back from ASCII to the external
representation for printing of the message in the |type_file|.

@<Set character onto page@>=
if good then
  with cur_seg^ do
  case loaded of
    yes:
      text:=text+chr(char_code);
    no, missing:
      finish_seg;       {needn't consider further}
  othercases
    do_nothing;
  endcases
else
  cur_seg^.text:=cur_seg^.text+xchr[ch]


@* Packed file format.
This format was designed by Tomas Rokicki in August, 1985.
@^Rokicki, Tomas@>

It constitutes a compact representation of the data contained in a
\.{GF} file.  The information content is the same, but packed (\.{PK}) files
are almost always less than half the size of their \.{GF} counterparts.  They
are also easier to convert into a raster representation because they do not
have a profusion of \\{paint}, \\{skip}, and \\{new\_row} commands to be
separately interpreted.  In addition, the \.{PK} format expressedly forbids
\&{special} commands within a character.  The minimum bounding box for each
character is explicit in the format, and does not need to be scanned for as in
the \.{GF} format.  Finally, the width and escapement values are combined with
the raster information into character ``packets'', making it simpler in many
cases to process a character.

A \.{PK} file is organized as a stream of 8-bit bytes.  At times, these bytes
might be split into 4-bit nybbles or single bits, or combined into multiple
byte parameters.  When bytes are split into smaller pieces, the `first' piece
is always the most significant of the byte.  For instance, the first bit of
a byte is the bit with value 128; the first nybble can be found by dividing
a byte by 16.  Similarly, when bytes are combined into multiple byte
parameters, the first byte is the most significant of the parameter.  If the
parameter is signed, it is represented by two's-complement notation.

The set of possible eight-bit values are separated into two sets, those that
introduce a character definition, and those that do not.  The values that
introduce a character definition comprise the range from 0 to 239; byte values
above 239 are interpreted commands.  Bytes which introduce character
definitions are called flag bytes, and various fields within the byte indicate
various things about how the character definition is encoded.  Command bytes
have zero or more parameters, and can never appear within a character
definition or between parameters of another command, where they would be
interpeted as data.

A \.{PK} file consists of a preamble, followed by a sequence of one or more
character definitions, followed by a postamble.  The preamble command must
be the first byte in the file, followed immediately by its parameters.
Any number of character definitions may follow, and any command but the
preamble command and the postamble command may occur between character
definitions.  The very last command in the file must be the postamble.

@ The packed file format is intended to be easy to read and interpret by
device drivers.  The small size of the file reduces the input/output overhead
each time a font is defined.  For those drivers that load and save each font
file into memory, the small size also helps reduce the memory requirements.
The length of each character packet is specified, allowing the character raster
data to be loaded into memory by simply counting bytes, rather than
interpreting each command; then, each character can be interpreted on a demand
basis.  This also makes it possible for a driver to skip a particular
character quickly if it knows that the character is unused.

@ First, the command bytes shall be presented; then the format of the
Character definitions will be defined.  Eight of the possible sixteen
commands (values 240 through 255) are currently defined; the others are
reserved for future extensions.  The commands are listed below.  Each command
is specified by its symbolic name (e.g., \\{pk\_no\_op}), its opcode byte,
and any parameters.  The parameters are followed by a bracketed number
telling how many bytes they occupy, with the number preceded by a plus sign if
it is a signed quantity.  (Four byte quantities are always signed, however.)

\yskip\hang|pk_xxx1| 240 |k[1]| |x[k]|.  This command is undefined in general;
it functions as a $(k+2)$-byte \\{no\_op} unless special \.{PK}-reading
programs are being used.  \MF\ generates \\{xxx} commands when encountering
a \&{special} string.  It is recommended that |x| be a string having the form
of a keyword followed by possible parameters relevant to that keyword.

\yskip\hang\\{pk\_xxx2} 241 |k[2]| |x[k]|.  Like |pk_xxx1|, but |0<=k<65536|.

\yskip\hang\\{pk\_xxx3} 242 |k[3]| |x[k]|.  Like |pk_xxx1|, but
|0<=k<@t$2^{24}$@>|.  \MF\ uses this when sending a \&{special} string whose
length exceeds~255.

\yskip\hang\\{pk\_xxx4} 243 |k[4]| |x[k]|.  Like |pk_xxx1|, but |k| can be
ridiculously large; |k| musn't be negative.

\yskip\hang|pk_yyy| 244 |y[4]|.  This command is undefined in general; it
functions as a five-byte \\{no\_op} unless special \.{PK} reading programs
are being used.  \MF\ puts |scaled| numbers into |yyy|'s, as a result of
\&{numspecial} commands; the intent is to provide numeric parameters to
\\{xxx} commands that immediately precede.

\yskip\hang|pk_post| 245.  Beginning of the postamble.  This command is
followed by enough |pk_no_op| commands to make the file a multiple
of four bytes long.  Zero through three bytes are usual, but any number
is allowed.
This should make the file easy to read on machines which pack four bytes to
a word.

\yskip\hang|pk_no_op| 246.  No operation, do nothing.  Any number of
|pk_no_op|'s may appear between \.{PK} commands, but a |pk_no_op| cannot be
inserted between a command and its parameters, between two parameters, or
inside a character definition.

\yskip\hang|pk_pre| 247 |i[1]| |k[1]| |x[k]| |ds[4]| |cs[4]| |hppp[4]|
|vppp[4]|.  Preamble command.  Here, |i| is the identification byte of the
file, currently equal to 89.  The string |x| is merely a comment, usually
indicating the source of the \.{PK} file.  The parameters |ds| and |cs| are
the design size of the file in $1/2^{20}$ points, and the checksum of the
file, respectively.  The checksum should match the \.{TFM} file and the
\.{GF} files for this font.  Parameters |hppp| and |vppp| are the ratios
of pixels per point, horizontally and vertically, multiplied by $2^{16}$; they
can be used to correlate the font with specific device resolutions,
magnifications, and ``at sizes''.  Usually, the name of the \.{PK} file is
formed by concatenating the font name (e.g., amr10) with the resolution at
which the font is prepared in pixels per inch multiplied by the magnification
factor, and the letters \.{PK}.  For instance, amr10 at 300 dots per inch
should be named AMR10.300PK; at one thousand dots per inch and magstephalf,
it should be named AMR10.1095PK.

@ We put a few of the above opcodes into definitions for symbolic use by
this program.

@d pk_id = 89 {the version of \.{PK} file described}
@d pk_xxx1 = 240 {\&{special} commands}
@d pk_yyy = 244 {\&{numspecial} commands}
@d pk_post = 245 {postamble}
@d pk_no_op = 246 {no operation}
@d pk_pre = 247 {preamble}

@ The \.{PK} format has two conflicting goals; to pack character raster and
size information as compactly as possible, while retaining ease of translation
into raster and other forms.  A suitable compromise was found in the use of
run-encoding of the raster information.  Instead of packing the individual
bits of the character, we instead count the number of consecutive `black' or
`white' pixels in a horizontal raster row, and then encode this number.  Run
counts are found for each row, from the top of the character to the bottom.
This is essentially the way the \.{GF} format works.
Instead of presenting each row individually, however, let us concatenate all
of the horizontal raster rows into one long string of pixels, and encode this
row.  With knowledge of the width of the bit-map, the original character glyph
can be easily reconstructed.  In addition, we do not need special commands to
mark the end of one row and the beginning of the next.

Next, let us put the burden of finding the minimum bounding box on the part
of the font generator, since the characters will usually be used much more
often than they are generated.  The minimum bounding box is the smallest
rectangle which encloses all `black' pixels of a character.  Let us also
eliminate the need for a special end of character marker, by supplying
exactly as many bits as are required to fill the minimum bounding box, from
which the end of the character is implicit.

Let us next consider the distribution of the run counts.  Analysis of several
dozen pixel files at 300 dots per inch yields a distribution peaking at four,
falling off slowly until ten, then a bit more steeply until twenty, and then
asymptotically approaching the horizontal.  Thus, the great majority of our
run counts will fit in a four-bit nybble.  The eight-bit byte is attractive for
our run-counts, as it is the standard on many systems; however, the wasted four
bits in the majority of cases seems a high price to pay.  Another possibility
is to use a Huffman-type encoding scheme with a variable number of bits for
each run-count; this was rejected because of the overhead in fetching and
examining individual bits in the file.  Thus, the character raster definitions
in the \.{PK} file format are based on the four-bit nybble.

@ The analysis of the pixel files yielded another interesting statistic: fully
37\char`\%\
of the raster rows were duplicates of the previous row.  Thus, the \.{PK}
format allows the specification of repeat counts, which indicate how many times
a horizontal raster row is to be repeated.  These repeated rows are taken out
of the character glyph before individual rows are concatenated into the long
string of pixels.

For elegance, we disallow a run count of zero.  The case of a null raster
description should be gleaned from the character width and height being equal
to zero, and no raster data should be read.  No other zero counts are ever
necessary.  Also, in the absence of repeat counts, the repeat value is set to
be zero (only the original row is sent.)  If a repeat count is seen, it takes
effect on the current row.  The current row is defined as the row on which the
first pixel of the next run count will lie.  The repeat count is set back to
zero when the last pixel in the current row is seen, and the row is sent out.

This poses a problem for entirely black and entirely white rows, however.  Let
us say that the current row ends with four white pixels, and then we have five
entirely empty rows, followed by a black pixel at the beginning of the next
row, and the character width is ten pixels.  We would like to use a repeat
count, but there is no legal place to put it.  If we put it before the white
run count, it will apply to the current row.  If we put it after, it applies
to the row with the black pixel at the beginning.  Thus, entirely white or
entirely black repeated rows are always packed as large run counts (in this
case, a white run count of 54) rather than repeat counts.

@ Now let us turn our attention to the actual packing of the run counts and
repeat counts into nybbles.  There are only sixteen possible nybble values.
We need to indicate run counts and repeat counts.  Since the run counts are
much more common, we will devote the majority of the nybble values to them.
We therefore indicate a repeat count by a nybble of 14 followed by a packed
number, where a packed number will be explained later.  Since the repeat
count value of one is so common, we indicate a repeat one command by a single
nybble of 15.  A 14 followed by the packed number 1 is still legal for a
repeat one count, however.  The run counts are coded directly as packed
numbers.

For packed numbers, therefore, we have the nybble values 0 through 13.  We
need to represent the positive integers up to, say, $2^{31}-1$.  We would
like the more common smaller numbers to take only one or two nybbles, and
the infrequent large numbers to take three or more.  We could therefore
allocate one nybble value to indicate a large run count taking three or more
nybbles.  We do this with the value 0.

@ We are left with the values 1 through 13.  We can allocate some of these, say
|dyn_f|, to be one-nybble run counts.
These will work for the run counts |1..dyn_f|.  For subsequent run
counts, we will use a nybble greater than |dyn_f|, followed by a second nybble,
whose value can run from 0 through 15.  Thus, the two-byte nybble values will
run from |dyn_f+1..(13-dyn_f)*16+dyn_f|.  We have our definition of large run
count values now, being all counts greater than |(13-dyn_f)*16+dyn_f|.

We can analyze our several dozen pixel files and determine an optimal value of
|dyn_f|, and use this value for all of the characters.  Unfortunately, values
of |dyn_f| that pack small characters well tend to pack the large characters
poorly, and values that pack large characters well are not efficient for the
smaller characters.  Thus, we choose the optimal |dyn_f| on a character basis,
picking the value which will pack each individual character in the smallest
number of nybbles.  Legal values of |dyn_f| run from 0 (with no one-byte run
counts) to 13 (with no two-byte run counts).

@ Our only remaining task in the coding of packed numbers is the large run
counts.  We use a scheme suggested by D.~E.~Knuth
@^Knuth, D.~E.@>
which will simply and elegantly represent arbitrarily large values.  The
general scheme to represent an integer |i| is to write its hexadecimal
representation, with leading zeros removed.  Then we count the number of
digits, and prepend one less than that many zeros before the hexadecimal
representation.  Thus, the values from one to fifteen occupy one nybble;
the values sixteen through 255 occupy three, the values 256 through 4095
require five, etc.

For our purposes, however, we have already represented the numbers one
through |(13-dyn_f)*16+dyn_f|.  In addition, the one-nybble values have
already been taken by our other commands, which means that only the values
from sixteen up are available to us for long run counts.  Thus, we simply
normalize our long run counts, by subtracting |(13-dyn_f)*16+dyn_f+1| and
adding 16, and then representing the result according to the scheme above.

@ The final algorithm for decoding the run counts based on the above scheme
might look like this, assuming a procedure called \\{pk\_nyb} is available
to get the next nybble from the file, and assuming that the global
|repeat_count| indicates whether a row needs to be repeated.  Note that this
routine is recursive, but since a repeat count can never directly follow
another repeat count, it can only be recursive to one level.

@<Packed number procedure@>=
function pk_packed_num : integer ;
var i, j, k : integer ;
begin
   i := get_nyb ;
   if i = 0 then begin
      repeat j := get_nyb ; incr(i) ; until j <> 0 ;
      while i > 0 do begin j := j * 16 + get_nyb ; decr(i) ; end ;
      pk_packed_num := j - 15 + (13-dyn_f)*16 + dyn_f ;
   end else if i <= dyn_f then
      pk_packed_num := i
   else if i < 14 then
      pk_packed_num := (i-dyn_f-1)*16+get_nyb+dyn_f+1
   else begin
      if i = 14 then
         repeat_count := pk_packed_num
      else
         repeat_count := 1 ;
      pk_packed_num := pk_packed_num ;
   end ;
end ;

@ For low resolution fonts, or characters with `gray' areas, run encoding can
often make the character many times larger.  Therefore, for those characters
that cannot be encoded efficiently with run counts, the \.{PK} format allows
bit-mapping of the characters.  This is indicated by a |dyn_f| value of
14.  The bits are packed tightly, by concatenating all of the horizontal raster
rows into one long string, and then packing this string eight bits to a byte.
The number of bytes required can be calculated by |(width*height+7) div 8|.
This format should only be used when packing the character by run counts takes
more bytes than this, although, of course, it is legal for any character.
Any extra bits in the last byte should be set to zero.

@ At this point, we are ready to introduce the format for a character
descripter.  It consists of three parts: a flag byte, a character preamble,
and the raster data.  The most significant four nybbles of the flag byte
yield the |dyn_f| value for that character.  (Notice that only values of
0 through 14 are legal for |dyn_f|, with 14 indicating a bit mapped character;
thus, the flag bytes do not conflict with the command bytes, whose upper nybble
is always 15.)  The next bit (with weight 16) indicates whether the first run
count is a black count or a white count, with a one indicating a black count.
For bit-mapped characters, this bit should be set to a zero.  The next bit
(with weight 8) indicates whether certain later parameters (referred to as size
parameters) are given in one-byte or two-byte quantities, with a one indicating
that they are in two-byte quantities.  The last two bits are concatenated on to
the beginning of the length parameter in the character preamble, which will be
explained below.

However, if the last three bits of the flag byte are all set (normally
indicating that the size parameters are two-byte values and that a 3 should be
prepended to the length parameter), then a long format of the character
preamble should be used instead of one of the short forms.

Therefore, there are three formats for the character preamble, and which one
is used depends on the least significant three bits of the flag byte.  If the
least significant three bits are in the range zero through three, the short
format is used.  If they are in the range four through six, the extended short
format is used.  Otherwise, if the least significant bits are all set, then
the long form of the character preamble is used.  The preamble formats are
explained below.

\yskip\hang Short form: |flag[1]| |pl[1]| |cc[1]| |tfm[3]| |dm[1]| |w[1]|
|h[1]| |hoff[+1]| |voff[+1]|.
If this format of the character preamble is used, the above
parameters must all fit in the indicated number of bytes, signed or unsigned
as indicated.  Almost all of the standard \TeX\ font characters fit; the few
exceptions are fonts such as \.{aminch}.

\yskip\hang Extended short form: |flag[1]| |pl[2]| |cc[1]| |tfm[3]| |dm[2]|
|w[2]| |h[2]| |hoff[+2]| |voff[+2]|.  Larger characters use this extended
format.

\yskip\hang Long form: |flag[1]| |pl[4]| |cc[4]| |tfm[4]| |dx[4]| |dy[4]|
|w[4]| |h[4]| |hoff[4]| |voff[4]|.  This is the general format which
allows all of the
parameters of the \.{GF} file format, including vertical escapement.
\vskip\baselineskip
The |flag| parameter is the flag byte.  The parameter |pl| (packet length)
contains the offset
of the byte following this character descripter, with respect to the beginning
of the |tfm| width parameter.  This is given so a \.{PK} reading program can,
once it has read the flag byte, packet length, and character code (|cc|), skip
over the character by simply reading this many more bytes.  For the two short
forms of the character preamble, the last two bits of the flag byte should be
considered the two most-significant bits of the packet length.  For the short
format, the true packet length might be calculated as |(flag mod 4)*256+pl|;
for the extended format, it might be calculated as |(flag mod 4)*65536+pl|.

The |w| parameter is the width and the |h| parameter is the height in pixels
of the minimum bounding box.  The |dx| and |dy| parameters are the horizontal
and vertical escapements, respectively.  In the short formats, |dy| is assumed
to be zero and |dm| is |dy| but in pixels;
in the long format, |dx| and |dy| are both
in pixels multiplied by $2^{16}$.  The |hoff| is the horizontal offset from the
upper left pixel to the reference pixel; the |voff| is the vertical offset.
They are both given in pixels, with right and down being positive.  The
reference pixel is the pixel which occupies the unit square in \MF; the
\MF\ reference point is the lower left hand corner of this pixel.  (See the
example below.)

@ \TeX\ requires that all characters which have the same character codes
modulo 256 also have the same |tfm| widths, and escapement values.  The \.{PK}
format does not itself make this a requirement, but in order for the font to
work correctly with the \TeX\ software, this constraint should be observed.
The current version of \TeX\ (1.5) cannot output character codes greater
than 255 anyway.

Following the character preamble is the raster information for the
character, packed by run counts or by bits, as indicated by the flag byte.
If the character is packed by run counts and the required number of nybbles
is odd, then the last byte of the raster description should have a zero
for its least significant nybble.

@ As an illustration of the \.{PK} format, the character \char4\ from the font
amr10 at 300 dots per inch will be encoded.  This character was chosen
because it illustrates some
of the borderline cases.  The raster for the character looks like this (the
row numbers are chosen for convenience, and are not \MF's row numbers.)

\vskip\baselineskip
\centerline{\vbox{\baselineskip=10pt
\halign{\hfil#\quad&&\hfil#\hfil\cr
0& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
1& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
2& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
3& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
4& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
5& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
6& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
7\cr
8\cr
9& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
10& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
11& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
12& & & & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M& & \cr
13& & & & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M& & \cr
14& & & & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M& & \cr
15& & & & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M& & \cr
16& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
17& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
18& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
19\cr
20\cr
21\cr
22& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
23& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
24& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
25& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
26& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
27& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
28&*& &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
&\hphantom{M}&\hphantom{M}\cr
}}}
The width of the minimum bounding box for this character is 20; its height
is 29.  The `*' represents the reference pixel; notice how it lies outside the
minimum bounding box.  The |hoff| value is $-2$, and the |voff| is~28.

The first task is to calculate the run counts and repeat counts.  The repeat
counts are placed at the first transition (black to white or white to black)
in a row, and are enclosed in brackets.  White counts are enclosed in
parentheses.  It is relatively easy to generate the counts list:
\vskip\baselineskip
\centerline{82 [2] (16) 2 (42) [2] 2 (12) 2 (4) [3]}
\centerline{16 (4) [2] 2 (12) 2 (62) [2] 2 (16) 82}
\vskip\baselineskip
Note that any duplicated rows that are not all white or all black are removed
before the repeat counts are calculated.  The rows thus removed are rows 5, 6,
10, 11, 13, 14, 15, 17, 18, 23, and 24.

@ The next step in the encoding of this character is to calculate the optimal
value of |dyn_f|.  The details of how this calculation is done are not
important here; suffice it to say that there is a simple algorithm which in one
pass over the count list can determine the best value of |dyn_f|.  For this
character, the optimal value turns out to be 8 (atypically low).  Thus, all
count values less than or equal to 8 are packed in one nybble; those from
nine to $(13-8)*16+8$ or 88 are packed in two nybbles.  The run encoded values
now become (in hex, separated according to the above list):
\vskip\baselineskip
\centerline{\tt D9 E2 97 2 B1 E2 2 93 2 4 E3}
\centerline{\tt 97 4 E2 2 93 2 C5 E2 2 97 D9}
\vskip\baselineskip\noindent
which comes to 36 nybbles, or 18 bytes.  This is shorter than the 73 bytes
required for the bit map, so we use the run count packing.

@ The short form of the character preamble is used because all of the
parameters fit in their respective lengths.  The packet length is therefore
18 bytes for the raster, plus
eight bytes for the character preamble parameters following the character
code, or 26.  The |tfm| width for this character is 640796, or {\tt 9C71C} in
hexadecimal.  The horizontal escapement is 25 pixels.  The flag byte is
88 hex, indicating the short preamble, the black first count, and the
|dyn_f| value of 8.  The final total character packet, in hexadecimal, is:
\vskip\baselineskip
$$\vbox{\halign{\hfil #\quad&&{\tt #\ }\cr
Flag byte&88\cr
Packet length&1A\cr
Character code&04\cr
|tfm| width&09&C7&1C\cr
Horizontal escapement (pixels)&19\cr
Width of bit map&14\cr
Height of bit map&1D\cr
Horizontal offset (signed)&FE\cr
Vertical offset&1C\cr
Raster data&D9&E2&97\cr
&2B&1E&22\cr
&93&24&E3\cr
&97&4E&22\cr
&93&2C&5E\cr
&22&97&D9\cr}}$$

@* Routines to access the packed pixel files.  We shall require routines to
access the packed file; sometimes this may even require access to individual
\&{bits}, but mostly we shall be accessing bytes or nybbles.

We also need a function that will get a single byte from the \.{PK} file.
Again, buffering may be done in this procedure.

@p function pk_byte : eight_bits ;
var temp : eight_bits ;
begin
   temp := pxl_file^[pk_loc mod VAX_block_length] ;
   pk_loc := pk_loc + 1 ;
   if (pk_loc mod VAX_block_length)=0 then
        get(pxl_file) ;
   pk_byte := temp ;
end ;

@ As we are reading the packed file, we often need to fetch (signed or
unsigned) 16 and 32 bit quantities.  Here we have three procedures to do this.

@p function get_16 : integer ;
var a : integer ;
begin a := pk_byte ; get_16 := a * 256 + pk_byte ; end ;
@#
function get_16_signed : integer ;
var a : integer ;
begin a := get_16; if a>32767 then a := a-65536;
  get_16_signed := a ; end ;
@#
function get_32 : integer ;
var a : integer ;
begin a := get_16 ; if a > 32767 then a := a - 65536 ;
get_32 := a * 65536 + get_16 ; end ;

@ Now we read and check the preamble of the pk file.  In the preamble, we
find the |hppp|, |dsize|, |checksum|.  After we've read the preamble, no
character locators will be available.

@d two_to_the_20th==@"100000

@<Read preamble@>=
pk_loc:=0; char_num:=-1; {Ensure that |char_num| is ``undefined''}
if pk_byte <> pk_pre then abort('Bad pk file,  pre command missing') ;
@:fatal error Bad pk file ...}{\quad\.{Bad pk file}@>
@:fatal error Bad pk file pre command missing}{\qquad\\{pre }\.{command missing}@>
if pk_byte <> pk_id then abort('Bad pk file, wrong version') ;
@:fatal error Bad pk file wrong version}{\qquad\.{wrong version}@>
pk_j := pk_byte ;
for pk_i := 1 to pk_j do dummy:=pk_byte ;       { Skip over PK file comment }
dsize := get_32 ;
checksum := get_32 ;
hppp := get_32 ; vppp := get_32 ;
magnification := round(hppp * 72.27 / 65536) ; {|* 5| in PKtoPX}
if hppp <> vppp then warning('PK font pixels non-square');
@:Warning: PK font pixels non-square}{\quad\.{PK font pixels non-square}@>
dsize:=(dsize/two_to_the_20th)*(magnification/resolution);
for pk_i:=0 to 255 do ch_loc[pk_i]:=0;

@ Of course, we need to define the above variables.

@<Glob...@>=
@!checksum : integer ; {checksum of pixel file}
@!hppp, @!vppp : integer ; {horizontal and vertical points per inch}
@!pk_loc : integer ; { byte currently being accessed in |pk_file| }
@!dummy : integer ; { used to ``absorb'' unwanted bytes from |pk_file| }

@* Character unpacking.
This is the procedure where we read the character packet and decode it into
standard pixel raster format; if a glyph is encountered that is not used in the
present document, its packet is skipped.  We create one row at a time, checking
for repeat
commands, and then repeat the row as many times as is necessary. The information
gleaned from the beginning of the character packet is to be placed in various
local variables. We also check that the length of the character packet is
correct.

If the character's raster is null (as is the case with the invisible fonts used
by Sli\TeX), the |num_cols| and |num_rows| are set to suitable dummy values (see
the section $\langle$Complete remainder of Character Definition
parameters$\rangle$ for further information).

@<Unpack and write character@>=
dyn_f := flag_byte div 16 ;
flag_byte := flag_byte mod 16 ;
turn_on := flag_byte >= 8 ;
if turn_on then flag_byte := flag_byte - 8 ;
if flag_byte = 7 then
   @<Read long character preamble@>
else if flag_byte > 3 then
   @<Read extended short character preamble@>
else
   @<Read short character preamble@> ;@/
byte_width:=(num_cols+7) div 8; {Bytes required per row in |lnf_file|}
ch_loc[char_num]:=end_of_packet-packet_length;
with glyph_map(TeX_font)(char_num) do
  if loaded=wanted then
  begin
     base:=ras_beg+ras_len;@/
     @<Process |tfm_width| information@>;
     @<Validate size of character's glyph@>;
     @<Complete remainder of Character Definition parameters@>;
     @<Read and translate raster description@>;
     @<Calculate the number of bytes of rasters transferred@>;
     @<Complete the entry in the font character directory@>
  end
  else
    @<Skip through unwanted raster@> ;
if end_of_packet <> pk_loc then abort('Bad pk file!  Bad packet length.')
@.Bad pk file@>

@ A simplified version of this code is also used when printing the bitmap of
an oversized glyph in sixels.

@<Unpack and store character@>=
dyn_f := flag_byte div 16 ;
flag_byte := flag_byte mod 16 ;
turn_on := flag_byte >= 8 ;
if turn_on then flag_byte := flag_byte - 8 ;
if flag_byte = 7 then
   @<Read long character preamble@>
else if flag_byte > 3 then
   @<Read extended short character preamble@>
else
   @<Read short character preamble@> ;@/
byte_width:=(num_cols+7) div 8; {Bytes required per row in |lnf_file|}
ch_loc[char_num]:=end_of_packet-packet_length;
if char_num=ch then
begin
   base:=ras_beg+ras_len;@/
   @<Read and translate raster description@>;
end
else
   @<Skip through unwanted raster@> ;
if end_of_packet <> pk_loc then abort('Bad pk file!  Bad packet length.')
@.Bad pk file@>

@ We need a whole lot of globals used but not defined up there.  Some of these
have the same names as certain fields of |pxl_dir_type|, or of local variables
of procedures |add_txf_to_lnf| and |copy_char|.  This is in order that the
segments of \.{WEB} code invoked above reference these globals therein, but the
appropriate local variables when within those procedures (when being used for
\.{PXL} files).

@<Glob...@>=
@!pk_i, @!pk_j : integer ; {index pointers}
@!flag_byte : integer ; {the byte that introduces the character definition}
@!end_of_packet : integer ; {where we expect the end of the packet to be}@/
{Following are also names of fields in a record of type |pxl_dir_type|}@/
@!num_cols, @!num_rows : sixteen_bits; {width and height of character}
@!x_off, @!y_off : signed_word ; {$x$ and $y$ offsets of character}
@!tfm_width : integer ; {character tfm width}
@#
@!tfms : array [0..255] of integer ; {character tfm widths}
@!dx, @!dy : integer ; {escapement values}
@!dxs, @!dys : array [0..255] of integer ; {escapement values}
@!ch_loc : array[0..255] of integer; {has the character been seen (and where)?}
@!dyn_f : integer ; {dynamic packing variable}
@!char_num : integer ; {the character we are reading}
@!packet_length : integer ; {the length of the character packet}
@!base : integer ; {index into |lnf_bytes|}
@!to_where : integer ; {\&{cf.} |to_where| in |copy_char|}
@!len : integer ; {\&{cf.} |len| in |copy_char|}
@!byte_width : integer ; {Bytes per row in the |lnf_file| bitmap}

@ To skip over the raster of an unwanted character, we can simply jump forward
in the PK file to |end_of_packet|.

@<Skip through unwanted raster@>=
while pk_loc<end_of_packet do dummy := pk_byte;

@ Now, the preamble reading modules.  First, we have the general case: the
long character preamble format.

@<Read long character preamble@>=
begin
   packet_length := get_32 ; char_num := get_32 ;
   end_of_packet := packet_length + pk_loc ;
   packet_length := packet_length + 9 ;
   tfm_width := get_32 ;
   dx := get_32 ; dy := get_32 ;
   num_cols := get_32 ;
   num_rows := get_32 ;
   x_off := get_32 ; y_off := get_32 ;
end

@ This module reads the character preamble with double byte parameters.

@<Read extended short character preamble@>=
begin
   packet_length := (flag_byte - 4) * 65536 + get_16 ;
   char_num := pk_byte ;
   end_of_packet := packet_length + pk_loc ;
   packet_length := packet_length + 4 ;
   pk_i := pk_byte ;
   tfm_width := pk_i * 65536 + get_16 ;
   dx := get_16 * 65536 ;
   dy := 0 ;
   num_cols := get_16 ;
   num_rows := get_16 ;
   x_off := get_16_signed ; y_off := get_16_signed ;
end

@ Here we read the most common character preamble, that with single byte
parameters.

@<Read short character preamble@>=
begin
   packet_length := flag_byte * 256 + pk_byte ;
   char_num := pk_byte ;
   end_of_packet := packet_length + pk_loc ;
   packet_length := packet_length + 3 ;
   pk_i := pk_byte ;
   tfm_width := pk_i * 65536 + get_16 ;
   dx := pk_byte * 65536 ;
   dy := 0 ;
   num_cols := pk_byte ;
   num_rows := pk_byte ;
   x_off := pk_byte ; y_off := pk_byte ;
   if x_off > 127 then x_off := x_off - 256 ;
   if y_off > 127 then y_off := y_off - 256 ;
end

@ Now we have the most important part of unravelling packed pixel files, where
we actually interpret the commands in the raster description.  First of all,
we need a procedure to get a single nybble from the file, as well as one to
get a single bit.

We also are now able to ``declare'' |pk_packed_num|.

@p function get_nyb : integer ;
var temp : eight_bits ;
begin
   if bit_weight = 0 then begin
      input_byte := pk_byte ;
      bit_weight := 16 ;
   end ;
   temp := input_byte div bit_weight ;
   input_byte := input_byte - temp * bit_weight ;
   bit_weight := bit_weight div 16 ;
   get_nyb := temp ;
end ;
@#
function get_bit : boolean ;
var temp : boolean ;
begin
   bit_weight := bit_weight div 2 ;
   if bit_weight = 0 then begin
      input_byte := pk_byte ;
      bit_weight := 128 ;
   end ;
   temp := input_byte >= bit_weight ;
   if temp then
      input_byte := input_byte - bit_weight ;
   get_bit := temp ;
end ;
@#
@t\4@>@<Packed number procedure@>

@ Now, the globals to help communication between these procedures.

@<Glob...@>=
@!input_byte : eight_bits ; {the byte we are currently decimating}
@!bit_weight : eight_bits ; {weight of the current bit}
@!nybble : eight_bits ; {the current nybble}
@!row_addr : integer ; {whereabouts in |lnf_file| of start of each pixel row}
@!bitmap_start : integer ; {and of start of \&{first} pixel row}

@ And the main procedure.

@<Read and translate raster description@>=
bit_weight := 0 ; row_addr:=base; {Note where row of pixels starts}
bitmap_start:=base;
if dyn_f = 14 then
   @<Get raster by bits@>
else @<Create normally packed raster@>

@ If |dyn_f=14|, then we need to get the raster representation
one bit at a time.

@<Get raster by bits@>=
begin
for pk_i := 1 to num_rows do begin
   bite:=0; bite_weight:=7;
   for pk_j := 1 to num_cols do begin
      if get_bit then bite:=bite+power[bite_weight];
      decr(bite_weight);
      if bite_weight<0 then begin
         bite_weight:=7;
         put_lnf(bite);
         bite:=0
      end
   end;
   if bite_weight<7 then put_lnf(bite)
end;
end

@ We need the |power| array, which contains powers of two, as well as the |bite|
variable (which accumulates eight pixels) and |bite_weight|, which directs which
pixel is to be ``blackened''.  The array |gpower| contains 0..8 black pixels,
staring at the left-hand end.

@<Globals...@>=
@!bite : integer; {accumulates 8 pixels to go to |lnf_file|}
@!bite_weight : integer; {next bit of |bite| to be filled (leftmost=0)}
@!power:array [0..7] of integer; {|power[i]| contains $2^{7-i}$}
@!gpower:array [0..8] of integer; {|gpower[i]| contains ones in leftmost |i| bits}

@ @<Set initial...@>=
power[0]:=128;
for i:=1 to 7 do power[i]:=power[i-1] div 2;
gpower[0]:=0;
for i:=1 to 8 do gpower[i]:=gpower[i-1]+power[i-1];

@ Otherwise, we translate the bit counts into the raster rows.  |run_ct|
contains the number of bits of the current color, and |turn_on| indicates
whether or not they should be black.  |rows_left| contains the number of
rows to be sent.

@<Create normally packed raster@>=
begin
rows_left := num_rows ;
h_bit := num_cols ;
repeat_count := 0 ;
bite:=0; bite_weight:=8;
while rows_left > 0 do begin
   run_ct := pk_packed_num ;
   while run_ct>0 do begin
      if (run_ct<bite_weight) and (run_ct<h_bit) then begin
         if turn_on then
            bite:=bite+gpower[bite_weight]-gpower[bite_weight-run_ct];
         h_bit:=h_bit-run_ct;
         bite_weight:=bite_weight-run_ct;
         run_ct:=0
      end
      else if (run_ct>=h_bit) and (h_bit<=bite_weight) then begin
         if turn_on then
            bite:=bite+gpower[bite_weight]-gpower[bite_weight-h_bit];
         put_lnf(bite); bite:=0;
         for pk_i:=1 to repeat_count do
            for pk_j:=1 to byte_width do repeat_row;
         row_addr:=base;
         rows_left := rows_left - repeat_count - 1 ;
         repeat_count := 0 ;
         bite:=0; bite_weight:=8;
         run_ct := run_ct - h_bit ;
         h_bit := num_cols ;
      end
      else begin
         if turn_on then bite:=bite+gpower[bite_weight];
         put_lnf(bite);
         run_ct:=run_ct-bite_weight;
         h_bit:=h_bit-bite_weight;
         bite:=0; bite_weight:=8
      end
   end;
   turn_on := not turn_on ;
end ;
if (rows_left <> 0) or (h_bit <> num_cols) then
   abort('Bad pk file---more bits than required!');
@.Bad pk file@>
end

@ We need to declare the repeat flag, bit counter, and color flag here.

@<Glob...@>=
@!repeat_count : integer ; {how many times to repeat the next row?}
@!rows_left : integer ; {how many rows left?}
@!turn_on : boolean ; {are we black here?}
@!h_bit : integer ; {what is our horizontal position?}
@!run_ct : integer ; {how many bits of current color left?}

@ Another necessary section of code skips over any \MF\ \.{special}s between
characters and before or after the postamble.

It is convenient to use the following definitions in creating readable |case|
statements; they will also be necessary for interpreting the \.{DVI} file.

@d four_cases(#)==#,#+1,#+2,#+3
@d eight_cases(#)==four_cases(#),four_cases(#+4)
@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)

@p procedure skip_specials ;
var i, j, k : integer ;
begin
   repeat
      flag_byte := pk_byte ;
      if flag_byte >= pk_xxx1 then
         case flag_byte of
            four_cases(pk_xxx1):
begin
   i := 0 ;
   for j := pk_xxx1 to flag_byte do i := 256 * i + pk_byte ;
   for j := 1 to i do dummy:=pk_byte ;
end ;
            pk_yyy: dummy:=get_32 ;
            pk_post: do_nothing;
            pk_no_op: do_nothing;
            pk_pre, eight_cases(pk_pre+1):
              abort('Bad pk file; unexpected byte: ', flag_byte:1,'!') ;
@.Bad pk file@>
         endcases ;
   until (flag_byte < pk_xxx1) or (flag_byte = pk_post) ;
end ;

@* Pixel file format.
A \.{PXL} file is an expanded raster description of a single font at a
particular resolution and contains essentially the same information as
that contained in a \.{GF} file.  \.{PXL} files are used by many existing
device-driver programs for dot matrix devices. By convention, \.{PXL} files
are for 200 pixels per inch. However, the LN03 requires pixel files generated
at 300 pixels per inch; the font naming conventions for access to these files
are specified elsewhere.

All words in a \.{PXL} files are in 32-bit format, with the four lower
bits zero on 36-bit machines. The raster information is contained in a
sequence of binary words which record white pixels as zeros and black
pixels as ones.

The first word of the \.{PXL} file and the last word contain the |pxl_id|
which is currently equal to 1001.

This first word is followed by a sequence of raster information words where
each line of pixels in the glyphs is represented by one or more words of
binary information. The number of words used to represent each row of pixels
for any particular glyph is fixed and it is set by the value of
|max_m-min_m+1| for that particular glyph. Each white pixel is represented by
a zero and each black pixel is represented by a one in the corresponding bit
positions (the first 32 only of each word on 36-bit machines). The unused bit
positions toward the end of each set of words for each row of pixels are
filled with zeros. It should be noted that this representation is more
wasteful of space than it needs to be, but it may possibly simplify the
subsequent use of the information by a device-driver program.

The font directory follows, occupying a fixed position with respect to the
end of the file (in words 517 through 6 from this end), and assigns 4
words for each of the potential 128 different glyphs that could be
contained in this particular font in the order of their ascending ascii
values (not in the order that the glyphs appear in the raster section,
which may be entirely arbitrary). This means that the first four words are
for the ascii zero glyph.  All four words reserved for any missing glyphs
are set to zero.  A detailed description of these directory entries is given in
the next section.

The final five words in the \.{PXL} file contain information relative to
the entire file.

The first of these five words is a checksum which should
match the checksum contained in the \.{TFM} file that \TeX\ used in
reference to this font, although, if this checksum is zero, no validity
checking will be done.

The second of these five words is an integer that is 1000 times the
magnification factor at which this font was produced.

The third word contains the design size of the font measured in \.{FIXes}
($2^{-20}$ unmagnified points).

The fourth word contains a pointer to the first word of the font directory.

The fifth (and last word of the entire file) contains a duplicate of the
|pxl_id| as contained in the first word of the file.

@d pxl_id=1001 {current version of \.{PXL} format}

@ As mentioned above, the pixel file's glyph directory consists of four
longwords for each possible glyph.  The use of each of these longwords is as
follows:

\yskip\hang\&{Word 0.} This is treated as being split into two unsigned
sixteen-bit quantities, containing the Pixel Width in the left half-word (the
leftmost 16 bits) and the Pixel Height in the right half-word (the next 16
bits). These dimensions are those of the smallest bounding-box, measured in
pixels, and they have nothing necessarily to do with the width and height
figures that appear in the \.{TFM} file.

\yskip\hang\&{Word 1.} Again is split into two sixteen-bit half-words, but
this time they are each treated as \\{signed} quantities. Together, these give
the offset of the glyph's reference point \&{from} its upper-left-hand corner
of the bounding box, measured in pixels, with the $x$-offset ($\delta x$) in the left
half-word and the $y$-offset ($\delta y$) in the right half-word. Two's complement
representation is used.  Remember that although the positive $x$ direction means
`rightward' and positive $y$ is `downward' on the page, these offsets specify
the distance \&{to} the reference point \&{from} the upper left-hand corner of
the raster with +ve $x$ implying that the left-most column of the raster is to
the \&{left} of the reference point, and +ve $y$ implying that the top-most
row of the raster is \&{above} the baseline.

\yskip\hang\&{Word 2.} This contains the number of the word in this \.{PXL}
file where the Raster Description for this particular glyph begins, measured
from the first word of the file, which is numbered zero.

\yskip\hang\&{Word 3.} Contains the \.{TFM} width, measured in \.{FIXes},
where 1 \.{FIX} is $1/(2^{20})$ times the design size.

We now define an appropriate structure to hold such directory entries:

@<Types in the outer block@>=
     @!signed_word= [word] -32768..32767;@/
     @!pxl_dir_type=packed record@/
        num_cols,num_rows : sixteen_bits;
        x_off,y_off :  signed_word;
        addr : [byte(4)] integer;
        tfm_width : [byte(4)] integer
     end;

@ We need a |file| object to reference the pixel file, and also an array to hold
its directory.  Furthermore, when a pixel file directory is read in, we need
variables |magnification| and |dsize| to hold quantities taken from the last
five longwords of the file.

@<Globals in the outer block@>=
  @!pxl_file: byte_file;
  @!pxl_dir : packed array [0..127] of pxl_dir_type;
  @!pxl_block,@!pxl_ptr : integer;
  @!pxl_size:integer;
  @!magnification,@!dsize:real;

@*Procedures to access the pixel file.  The following definitions and
declarations provide a means of accessing bytes, words (signed \AM\ unsigned)
and longwords in the |pxl_file|.

@d pxl_byte(#)==pxl_file^[#]

@d pxl_word(#)==(pxl_byte(#+1)+256*pxl_byte(#))

@d pxl_long(#)==(pxl_byte(#+3)+256*(pxl_byte(#+2)+@|256*(pxl_byte(#+1)+@|
256*pxl_byte(#))))

@d must_get(#)==
  if # = VAX_block_length then
  begin  get(pxl_file,VAX_continue);
    incr(pxl_block); #:=0
  end

@p  procedure move_to_pxl(n:integer);
  begin
    if n div VAX_block_length <> pxl_block then
    begin pxl_block:=n div VAX_block_length;
      VAX_find_block(pxl_file,pxl_block+1,VAX_continue)
    end;
    pxl_ptr:=n mod VAX_block_length
  end;

@ The |long_pxl| function reads a longword (in Bigendian order) from the
|pxl_file| and converts it to correct (signed) VAX longword notation.

@p  function long_pxl(var index:integer) : integer;
  begin
    if pxl_byte(index)<=@"80 then
      long_pxl:=pxl_long(index)
    else
      long_pxl:=pxl_byte(index+3)+256*(pxl_byte(index+2)+256*(
                pxl_byte(index+1)+256*(pxl_byte(index)-256)));
    index:=index+4;
    must_get(index)
  end;

@ The |sign_pxl_word| function reads a signed shortword (two bytes).

@p  function sign_pxl_word(var index:integer) : integer;
  begin
    if pxl_byte(index)>=@"80 then
      sign_pxl_word:=pxl_word(index)-@"10000
    else
      sign_pxl_word:=pxl_word(index);
    index:=index+2;
    must_get(index)
  end;

@ The |word_pxl| function reads an unsigned two-byte word.

@p  function word_pxl(var index:integer) : integer;
  begin word_pxl:=pxl_word(index);
    index:=index+2; must_get(index)
  end;

@*Handle rasters of oversized glyphs.  Because of a physical limitation of the
LN03, it is not legal to download the glyph of a character which would occupy
more than 5,700 bytes.  (Details appear under ``\&{Copy one character's
rasters}''.)

Any such glyph which has not been downloaded will have its |loaded| flag set to
|no| in its |glyph_map| entry.  When we attempt to add such a character to
the output lists, we add instead a separate segment which indicates that it
holds a single oversized glyph.  When this segment is eventually processed,
we output the glyph itself as a bitmap dump, using sixel graphics.

To save us having to keep on opening and closing the pixel file containing
the glyph, we maintain the ``name'' of the currently open file in a global
string, and only open a new file if the glyph required comes from a
different \TeX\ font. The global variable |open_file_name| is a textual
representation of the full file specification of the currently open pixel
file. (Note that this same variable name also appears as a \&{formal
parameter} of the |add_txf_to_lnf| function; it is referenced in the
$\langle$Open the file containing the glyph bitmaps$\rangle$ W{\mc EB}
section, used in the following code, and the aforementioned function.)

@<Globals...@>=
@!open_file_name : file_spec;

@ Of course, when we start (and even after we've created the font file(s) for
down-loading) there is no pixel file open.  Let's indicate that:

@<Set initial...@>=
open_file_name:='';

@ Procedure |print_glyph| has to refer to |change_pixel_file|, which is more
convenient to declare along with the font downloading procedures.  So we'll
cheat and put a |forward| declaration here!

@p procedure change_pixel_file( TeX_font : integer); forward;

@ The procedure |print_glyph| opens the associated pixel file (if necessary)
by calling |change_pixel_file|.  It firstly outputs absolute positioning
commands to establish the correct ``printing'' position (the strange
|bitmap_offset| was incorporated when the reference to the glyph was stored
in the |segment|), and then outputs suitable escape sequences to locate the
printing position at the correct offset relative to that position, as
determined by the glyph's first pixel relative to its reference point.

If the pixels come from a |pixel_file|, then we can locate the required
character glyph through the character directory.  In the case of a
|packed_file|, it may happen than this character has already been output, and
so its position within the file is known; in this case either it was the last
character read (has already been dumped as a bitmap), when its glyph will be in
the |lnf_file|: otherwise we position |pk_loc| to the correct position in the
file.  If the character has never been read (|ch_loc=0|) then we read and
process characters into the \&{same} part of the |lnf_file| until the desired
character has been read.  When reading |packed_file|s, we utilize an entry in
the |pxl_dir| in preference to the globals |x_off|, etc, so save repetition of
many lines of \.{WEB} code.

The lines of pixel rasters are then output, six at a time, in sixel graphics
format.

@p procedure print_glyph(@!glyph_seg : segment; @!y_loc : integer);
  var  @!sixel_line : array [1..6,1..300] of eight_bits;
    @!i,@!j,@!k,@!l,@!m,@!n,@!len : integer;
    @!last_sixel,@!matching: integer;
    @!ch : integer;
  @<Declare the procedure |put_sixel|@> ;
begin
  write_ln(ln3_file);
  change_pixel_file(glyph_seg.font);@/
  write(ln3_file,csi,y_loc:1,'d',csi,glyph_seg.xs:1,'`');
  ch:=glyph_seg.ch;
  with pxl_dir[ch] do
  begin
    @<Read raster for oversized glyph into memory@> ;
    @<Prepare to write glyph bitmap at correct position@> ;
    write_ln(ln3_file,dcs,'9;0;1q');    {Prepare to meet a sixel dump!}
    @<Convert glyph's raster to sixels@>;
    write_ln(ln3_file,st);              {Finish sixel dump}
  end; {with}
end;

@ We are making use of a record of |pxl_dir| to hold the various quantities
regarding the dimensions of the glyph which have been (or are about to be) read
from the font file.  In the case of an unpacked raster, we need only position
the file to the correct longword, but for a packed file, it's now time to
start reading the bytes and reconstituting the raster in a free part of the
|lnf_file|.

@<Read raster for oversized glyph into memory@>=
if pxl_ident=pixel_file then move_to_pxl(addr*4)
else begin
  ras_beg:=2*VAX_block_length;
  if ch_loc[ch]=0 then
    @<Find packed raster and read into memory@> ;
  if char_num<>ch then
    @<Move back in packed file and read raster into memory@> ;
  base:=ras_beg
end;

@ If the character has not yet been read from the packed font file, we need to
read forwards until the character is met.  We then are able to access its
raster, which will have been created in memory as the character was read.
The character cannot be missing or the font download would not have resulted
in the |loaded| field having the value |no|.

@<Find packed raster and read into memory@>=
repeat ras_len:=0;@/
   @<Unpack and store character@>;
   skip_specials
until (ch_loc[ch]<>0) or (flag_byte=pk_post);

@ If we've previously read the wanted character's glyph, but it has since been
superseded by another's, we need to backtrack the packed font file to the
correct position and read the raster all over again.

@<Move back in packed file and read raster into memory@>=
begin {character not already in |lnf_file|}
   pk_loc:=ch_loc[ch];
   VAX_find_block(pxl_file,(pk_loc div VAX_block_length)+1,VAX_continue);
   ras_len:=0; flag_byte:=pk_byte; {We know there's no \.{special}s}@/
   @<Unpack and store character@>;
   skip_specials
end

@ Having read the glyph into memory, we now need to position the LN03 to start
writing its first sixel at the appropriate position relative to the reference
point.  We do this by sending Horizontal and Vertical Positioning control
sequences, either Relative for forward movement, or Backward when negative
movement is necessary.  The signs are reversed because the offsets are those
of the top left-most pixel of the glyph relative to the reference point.

@<Prepare to write glyph bitmap at correct position@>=
if x_off<0 then
  write(ln3_file,csi,abs(x_off):1,'a')  {Horizontal Position Relative}
else
  if x_off>0 then write(ln3_file,csi,x_off:1,'j');{Horizontal Position Backward}
if y_off<0 then
  write(ln3_file,csi,abs(y_off):1,'e')  {Vertical Position Relative}
else
  if y_off>0 then write(ln3_file,csi,y_off:1,'k') {Vertical Position Backward}

@ This routine outputs the sixel character, |x|, in the optimum sixel encoding
making use of repeat counts if the same sixel is repeated more than twice.
The current output record is terminated if it's getting too long, and a new
one commenced.

@<Declare the procedure |put_sixel|@>=
procedure @!put_sixel(x:integer);
begin
  if x = last_sixel then incr(matching) else
    @<Output |matching| copies of |last_sixel|@> ;
  if len >=128 then
  begin
    write_ln(ln3_file);
    len:=0
  end
end

@ If we have a new sixel (|x|) which differs from the previous one
(|last_sixel|) we have to output the latter the correct number of times.  The
number of successive calls of |put_sixel| will have been counted in
|matching|.  After performing the output, we record that |x| has now been
``output'' once.

@<Output |matching| copies of |last_sixel|@>=
begin
  if matching < 3 then
    @<Output explicit copies of |last_sixel|@>
  else
    @<Output repeat count for |last_sixel|@> ;
  matching:=1;
  last_sixel:=x
end

@ If |last_sixel| has only been output once or twice, we send the explicit
copies of it

@<Output explicit copies of |last_sixel|@>=
begin
  len:=len+matching;
  while matching > 0 do
  begin
    write(ln3_file,chr(last_sixel));
    decr(matching)
  end
end

@ On the other hand, it's more efficient to use a sixel repeat count if there
are three or more copies of |last_sixel| required.  The introducer for such a
repeat count is the `\.!' character, which is followed by the ASCII character
representation of the count.  The actual sixel to be output then follows this
count.

@<Output repeat count for |last_sixel|@>=
begin
  write(ln3_file,'!',matching:1,chr(last_sixel));
  len:=len+5
end

@ To produce the bitmap sixel dump of the glyph, we read the pixels into the
array |sixel_line|; this contains 6 rows, each capable of holding 300
|eight_bits|, containing 8 pixels of the character's raster.  We index this with
|k|, which thus runs |1..6|

Variable |i| is used to run through the |num_rows| lines of rasters, whilst |j|
indexes the bytes from the pixel file, and into |sixel_line|.

@<Convert glyph's raster to sixels@>=
i:=0; k:=1;
while i < num_rows do
begin
  @<Read rows of pixels into sixel buffer@>;
  incr(k); incr(i);
  if k > 6 then
  begin
    @<Output line of sixels@>;
    k:=1
  end
end;
if k>1 then
begin
  while k <= 6 do
  begin   {Fill in bottom incomplete rows of sixels}
    for j:=1 to (num_cols+7) div 8 do
      sixel_line[k,j]:=0;
    incr(k)
  end;
  @<Output line of sixels@>
end

@ The following section outputs the six rows of pixels in
|sixel_line| as $8\times{|num_cols|}$ columns of sixel graphics.

The variable |m| runs through the values 128,64,32,..,1, and is used to divide
an |eight_bit| to extract the most-significant bit of eight pixels in rows 6..1
(indexed by |n|) of |sixel_line|.  Each bit is transferred into the least
significant bit of |l|, initially zeroed for each sixel.

As each sixel is completed, it is output to the \.{LN3} file.  To prevent the
output of too many characters to the |ln3_file|, which is of type |text|, the
output is broken every 128 bytes.  A ``graphics'' end-of-record is inserted
after every line of sixels.

@<Output line of sixels@>=
len:=0; last_sixel:=0; matching:=0;
for j:=1 to (num_cols+7) div 8 do
begin
  m:=128;
  while m>0 do
  begin
    l:=0;
    for n:=6 downto 1 do
      if odd(sixel_line[n,j] div m) then
        l:=2*l + 1
      else
        l:=2*l;
    put_sixel(l+63);
    m:=m div 2
  end;
end;
put_sixel(0);   {Flushes the |last_sixel| from the ``buffer''}
write_ln(ln3_file,'-')  {Sixel `CR-LF'}


@ We are now in a position to be able to include the declaration of |ord_text|
into the \PASCAL\ generated from the \.{WEB}

@p @<Declare the |ord_text| Procedure@>;
@*Output the individual segments forming a page.  At the end of each page, we
have a number of linked lists containing each of the segments set on any
individual ``line'' of the paper.  We have to re-order these, such that they
are output in the most efficient manner, with appropriate changes of (lnf)
font as necessary.

When pages are actually being ``set'', |font_in_use[lnf]| will contain either
-1, which indicates that the LN03 font required is not currently mapped to a
|SGR| (Select Graphic Rendition) code, or a number in the range |0..9|, from
which the |SGR| font number to be used to invoke that font as the current one is
derived (by adding 10).

The array |who_uses| maps these |SGR| indices back to the number of an LN03
font.  Finally, the current |SGR| in use is held in |cur_out|, to minimise
the number of |SGR| commands that need to be included in the output stream.

@<Globals...@>=
@!font_in_use:array [0..max_lnfonts] of -1..max_SGR;
@!who_uses:array [0..max_SGR] of -1..max_lnfonts;
@!cur_out:0..max_SGR;

@ First of all, we shall require a procedure to output an arbitrary string
(most commonly an escape sequence) to the LN03 printer.  This will typically
be used to activate the |HPA| (Horizontal Position Absolute) control sequence.

@p  procedure esc_out(str:varying[str_len] of char);    {Escape sequence to LN3}
  begin
    ln3_cnt:=ln3_cnt+VAX_length(str);
    write(ln3_file,str)
  end;

@ Then we require a procedure to output as much of a segment as
possible to the current line, and delete that section from the segment as
stored before returning.

One useful extension of VAX-\PASCAL\ that we utilize are the functions |min|
and |max|.  These each take an arbitrary number of parameters, and return a
result, of the same type, that is equal to the smallest or largest
(numerically) of the parameters provided.

@d VAX_min==@= min@>
@d VAX_max==@= max@>

@p  procedure print_seg;
  var @!text_len,@!uselen,@!i,@!j,@!ln_font,@!delta_x:integer;
    @!csi_present:integer;
  begin
    with cur_seg^ do
    begin
      text_len:=VAX_length(text);
      uselen:=VAX_min(text_len,max_line-ln3_cnt,max_print-ln3_print);
      if (uselen>0) or (text_len=0) then        {`Use' empty segment}
      begin     {Otherwise, throw segment back if none of it will fit!}
        if uselen <> text_len then              {Text won't all fit!}
          @<Determine position at which to split output line@>;
        @<Select correct font, if necessary@> ;
        @<Set horizontal position on paper@>;
        write(ln3_file,substr(text,1,uselen));
        @<Count characters output to printer, perform split if necessary@>
      end
    end
  end;

@ If a segment is too long to fit in its entirety upon the output line to be
sent to the LN03, we split it into smaller segments.  This cannot be done at
an arbitrary cut-off point, since the segment might include embedded control
sequences (which will be |HPR| ones, ending with `\.a'); therefore, we need to
select a split point which will not destroy the contiguity of such sequences.

@<Determine position at which to split output line@>=
begin
    csi_present:=0;
    @<Find rightmost \.{ESC} or \.{CSI} character in segment@> ;
    @<Split \\{after} the |HPR| control sequence, if in last 8 characters@>
end

@ Finding the right-most control sequence starts off by locating the character
which introduces it;  this used to be \.{ESC} but nowadays the eight-bit
\.{CSI} character is used instead.

@<Find rightmost \.{ESC} or \.{CSI} character in segment@>=
for i:=1 to uselen do
    if (text[i] = csi) or (text[i] = esc) then csi_present:=i

@ If that right-most control sequence introducer was earlier than eight
characters from the end of the part of the string that we \\{can} use (or
didn't exist) there's no need to split the segment.  Otherwise, we need to
locate the `\.a' which completes the Horizontal Position Relative sequence,
and split after it.

@<Split \\{after} the |HPR| control sequence, if in last 8 characters@>=
if (csi_present>0) and ((uselen-csi_present)<=8) then
begin i:=csi_present;
  while (i<uselen) and (text[i]<>'a') do incr(i);
  if text[i]<>'a' then uselen:=csi_present-1
end

@ Whenever we output a segment, we must ensure that the LN03 will image the
characters therein from the correct downloaded font.  The following code
firstly checks whether the font is currently designated (and thus selectable)
within the LN03.  It then selects the necessary font by outputting a suitable
|SGR| (Select Graphic Rendition) to access the appropriately designated font.

@<Select correct font, if necessary@>=
if font_in_use[font]<0 then     {not currently designated}
@<Select Graphic Rendition@>;
if font_in_use[font]<>cur_out then
begin cur_out:=font_in_use[font];@/
  esc_out(csi+str_int(10+cur_out)+'m'); {|SGR| for the font}
end

@ Although we permit a maximum of |max_lnfonts| to have been downloaded to the
LN03, the machine itself may only access one of ten of these through the
ISO-standard |SGR| (Select Graphic Rendition) control sequence.  If a
different font is required, it may be necessary to designate that it shall be
selectable by a different |SGR| sequence.

@<Select Graphic Rendition@>=
begin
    font_in_use[who_uses[max_SGR]]:=-1;
    font_in_use[font]:=max_SGR;
    who_uses[max_SGR]:=font;
    esc_out(dcs+'1;'+str_int(10+max_SGR)+'}U0000'+@=dec@>(font,2,2)+
                    '002SK00GG'+st)
end


@ We need to output appropriate ISO-standard control sequences to position the
segment at the correct position on the current line; if the segment is the
first to be output on the current line, this positioning should use the |HPA|
(Horizontal Position Absolute) sequence, otherwise the |HPR| (Horizontal
Position Relative) sequence is preferred.

@<Set horizontal position on paper@>=
if cur_seg=out_list then                {Leftmost segment of line}
    esc_out(csi+str_int(xs)+'`')        { HPA to |xs|}
else@/
begin
    delta_x:=xs-prev_seg^.xe;   {Distance from end of next leftward segment}
    if delta_x>0 then esc_out(csi+str_int(delta_x)+'a') {HPR}
    else if delta_x<0 then abort('Internal error (-ve delta x)')
@:fatal error Internal error ...}{\quad\.{Internal error}@>
@:fatal error Internal error negative delta x}{\qquad\.{(-ve delta x)}@>
end

@ After we have output the first |uselen| characters to the \.{LN3} file, we
then check whether that's finished off this segment.  If it has, we can
discard the segment, but otherwise we perform the split at the predetermined
point.

@<Count characters output to printer, perform split if necessary@>=
ln3_cnt:=ln3_cnt+uselen; ln3_print:=ln3_print+uselen;
if uselen=text_len then
  cur_seg:=nil                  {All of segment used}
else
  @<Split segment which would create too long an output line@>

@ Having previously determined the split point for the segment (including none
at all), we now reduce the segment to the part following that point.

This is complicated by the fact that we must determine whereabouts we have
actually got to on the current line.  To do this, we increment the ``current
position'' by each character's width, whilst any |HPR| sequences have to be
``read'' in as numbers and added to the count.

@<Split segment which would create too long an output line@>=
begin i:=1;
  while i<=uselen do
  begin
    if (text[i]<>esc) and (text[i]<>csi) then
      xs:=xs+ch_widths[font,ord(text[i])]
    else
      @<Read back the horizontal skip specified in the |HPR| sequence@> ;
    incr(i);
  end;
  text:=substr(text,uselen+1,text_len-uselen)
end

@ Whenever we meet a \.{ESC} or \.{CSI} character, we assume that it starts a
Horizontal Position Relative command, locate the first digit of its parameter
(which will follow a `\.[' after the \.{ESC} or come immediately after the
\.{CSI}), and the `\.a' which finishes the control sequence.  The characters in
between represent a decimal number, which we can ``read'' using a Vax-\PASCAL\
extension.  The current placing position is then incremented by this amount.

@<Read back the horizontal skip specified in the |HPR| sequence@>=
begin
  if text[i]=esc then j:=i+2 else j:=i+1;
  while text[j]<>'a' do incr(j);
  if text[i]=esc then
    VAX_string_read(substr(text,i+2,j-i-2)+' ',delta_x)
  else
    VAX_string_read(substr(text,i+1,j-i-1)+' ',delta_x);
  xs:=xs+delta_x; i:=j;
end



@ As we shall see later, if the string of characters in a \TeX\
\.{\\special} command commences with the character string |'SX '| (i.e.,
`old style'), or |'ln03:plotfi[le] '| or |'ln03:sixel '| (`new style'), a
segment will be inserted into the page list referencing the appropriate
file; when the time comes to process that segment, the following procedure
will be invoked.  The |text| field of |cur_seg| will be the name of a
VAX/VMS file which is assumed to contain an image encoded into DEC's
standard ``sixel'' format.

This file is then opened, and the records therein copied to the \.{.LN3}
file; the |y_position| parameter, and the starting position recorded in
field |xs| are used in appropriate escape sequences to position this
graphics dump at the desired position on the page before this operation.

The name of the file included is reported on the terminal and in the log file.
@p procedure Copy_sixel_file(@!y_position:integer);
var
  @!open_stat : integer; {records whether |open| was successful}
  @!sixel_record, {holds records being copied from included file}
  @!stat_msg : varying [256] of char; {just holds an error indication}
  @!sixel_file : text;  {The file of graphics details to be copied}
begin
  open(sixel_file,cur_seg^.text,@=readonly@>,@=256@>,
         @=user_action@>:=file_open,VAX_continue);
  open_stat := status(sixel_file);
  if open_stat<>0 then
  begin
    case open_stat of
      2: stat_msg := 'PAS-E-ERRDUROPE';
      3: stat_msg := 'PAS-E-FILNOTFOU';
      5: stat_msg := 'PAS-E-ACCMETINC';
      6: stat_msg := 'PAS-E-RECLENINC';
      7: stat_msg := 'PAS-E-RECTYPINC';
      8: stat_msg := 'PAS-E-ORDSPEINC';
    othercases
       stat_msg := str_int(open_stat);
    endcases;
  error('Couldn''t open \special inclusion: ',cur_seg^.text,
         ' - ignored (status=',stat_msg,')');
@:Error: Couldn't open special inclusion}{\quad\.{Couldn't open \\special inclusion}@>
  end else
  begin
    monitor(' ('+def_file_name);
    print_ln('Sixel graphics included from: ',def_file_name);
@.Sixel graphics included...@>
    reset(sixel_file);
    write(ln3_file,csi,y_position:1,'d',csi,cur_seg^.xs:1,'`');
    while not eof(sixel_file) do
    begin
      read_ln(sixel_file,sixel_record);
      write_ln(ln3_file,sixel_record)
    end;
    close(sixel_file,@=disposition:=save@>,VAX_continue);
    monitor(') ');
    write_ln(ln3_file,SSU_pixel);     {Make sure that we still have pixel SSUs}
  end;
end;

@ Whilst traversing the lists, we require some additional variables:
During this output phase, we split the lines
of output such that no line contains more than |max_line| characters
altogether, of which no more than |max_print| may be printable ones.  The
counters |ln3_cnt| and |ln3_print| permit us to exercise this control.  The
|boolean| variable |top_of_page| controls the inclusion of a |FF| character in
the output file to split the pages.

If the page contains glyphs in both of the possible orientations, this is
recorded in |two_orientations|.  Segments corresponding to the orientation
not currently being output are shuffled off onto the |deferred_list| whilst
this processing takes place.

@<Globals in the ...@>=
  @!out_list,@!prev_seg,@!right_seg,@!left_seg:^segment;
  @!x_left,@!x_max:integer;
  @!ln3_cnt,@!ln3_print:integer;
  @!still_fits,top_of_page:boolean;
  @!two_orientations:boolean;
  @!deferred_list:^segment;


@ The actual traversing of the lists takes place here; since we now support
the concept of mixing orientations on the one page, it's sometimes necessary
to traverse the lists twice!

@<Output contents of page@>=
repeat
  two_orientations := false;  {assume that we \&{don't} need two passes!}
  for k:=y_top to y_bot do    {Cycle through each possible list}
    if seg_list[k] <> nil then
    begin
      out_list:=nil; deferred_list:=nil;
      repeat                    {until we've emptied current line}
        @<Save segments which require opposite orientation@> ;
        @<Process bitmaps and other ``empty'' segments@>;
        @<Construct a list of non-overlapping segments@> ;
        @<Output list of non-overlapping segments@> ;
      until seg_list[k]=nil;
      if deferred_list<>nil then two_orientations:=true;
      seg_list[k]:=deferred_list
    end;
    if two_orientations then {prepare for second (and last) pass}
    begin
      if cur_orient=landscape then new_orient:=portrait
      else new_orient:=landscape;
      @<Change page orientation@>
    end;
until not two_orientations;
if lost_seg <> nil then
begin cur_seg:=lost_seg;
  finish_seg            {Output remaining errors}
end;

@ If, when outputting stored information in |cur_orient|, we encounter some
segment(s) in the opposite orientation, we save them in a subsidiary list,
and stuff them back into |seg_list[k]| after processing the current row.

@<Save segments which require opposite orientation@>=
prev_seg:=nil; cur_seg:=seg_list[k];
repeat
  right_seg:=cur_seg^.next; {we know that |cur_seg<>nil|}
  if cur_seg^.direction<>cur_orient then
  begin
    if prev_seg<>nil then
      prev_seg^.next:=right_seg   {in middle of list}
    else
      seg_list[k]:=right_seg;     {at beginning of list}
    cur_seg^.next:=deferred_list; {add segment to front of deferred list}
    deferred_list:=cur_seg
  end else
    prev_seg:=cur_seg;            {latest non-empty node}
  cur_seg:=right_seg              {next node on |seg_list|}
until cur_seg=nil


@ Now that the program can support landscape and portrait orientation within
the same document, we need a method of telling the LN03 to switch between
the modes.  This code is called before output of the first object to the
page, if |new_orient<>orientation|, and again at the end of the page, to
reset to the default |orientation| for the next page.

@<Change page orientation@>=
if new_orient<>cur_orient then
begin
  if new_orient=landscape then
  begin
    write_ln(ln3_file,csi,PFS_landscape);
    page_len:=paper_wid+page_x_min; page_wid:=paper_ht+y_land_min;
    y_min:=page_x_min; x_min:=y_land_min
  end else
  begin
    write_ln(ln3_file,csi,PFS_portrait);
    page_len:=paper_ht+y_port_min; page_wid:=paper_wid+page_x_min;
    y_min:=y_port_min; x_min:=page_x_min
  end;
  cur_orient:=new_orient
end

@ When handling characters from a virtual font, the situation can arise in
which a totally empty segment (one that contains no text) has been added to
the list.  Such segments can be discarded!  Other segments can appear to
contain no text, but actually reference a single oversized glyph, which must
therefore be output as a bitmap.

@<Process bitmaps and other ``empty'' segments@>=
prev_seg:=nil; cur_seg:=seg_list[k];
if cur_seg<>nil then
repeat
  right_seg:=cur_seg^.next; {we know that |cur_seg<>nil|}
  if (VAX_length(cur_seg^.text)=0) or (cur_seg^.seg_type<>glyph_string) then
  begin
    @<Output segment which references an external bitmap@>;
    if prev_seg<>nil then
      prev_seg^.next:=right_seg   {in middle of list}
    else
      seg_list[k]:=right_seg;     {at beginning of list}
    cur_seg^.next:=free_seg_list; {add segment to front of free list}
    free_seg_list:=cur_seg
  end else
    prev_seg:=cur_seg;            {latest non-empty node}
  cur_seg:=right_seg              {next node on |seg_list|}
until cur_seg=nil

@ Character glyphs which were too large to be downloaded into the LN03's
internal fonts have the |seg_type| field of the segment set to |bitmap|;
when we meet such a segment, we output the bitmap immediately.  Similarly,
sixel dumps referenced in \.{\\special} commands have the value |sixel_dump|
for this field, and |Copy_sixel_file| is called to copy the referenced file.

@<Output segment which references an external bitmap@>=
case cur_seg^.seg_type of
  bitmap: print_glyph(cur_seg^,k);
  sixel_dump: Copy_sixel_file(k);
  othercases do_nothing;
endcases

@ For as many as possible of all the segments which belong at the current
absolute vertical position, we construct a list in which none of the segments
overlap each other.  We are then able to output these in one sequence which
will contain only characters and \&{forward} horizontal movement commands.

@<Construct a list of non-overlapping segments@>=
x_left:=1000000000;     {A very high value; ensure |right_seg| \AM\ |left_seg| get set}
repeat
  prev_seg:=nil; right_seg:=nil;
  x_max:=-1;
  cur_seg:=seg_list[k];
  @<Find segment of current line which extends furthest to the right@> ;
  @<Transfer right-most segment to the |out_list|@>
until right_seg = nil;

@ We start by locating the segment (of those available) which extends furthest
to the right, and also the one which precedes it.  These are then pointed to
by |right_seg| and |prev_seg|, respectively.

@<Find segment of current line which extends furthest to the right@>=
while cur_seg<>nil do
begin
  if (cur_seg^.xe > x_max) and (cur_seg^.xe <= x_left) then
  begin x_max:=cur_seg^.xe;
    right_seg:=cur_seg;         {Points to rightmost segment}
    left_seg:=prev_seg  {Points to first of at least 2, or is |nil|}
  end;
  prev_seg:=cur_seg;
  cur_seg:=cur_seg^.next;
end

@ At this point, we have |right_seg| pointing to the segment which extends
furthest to the right, whilst |left_seg| will point to the segment immediately
before this (which may, or may not, overlap |right_seg|).

We then put it at the front of the |out_list|; further non-overlapping
segments will later be placed in front of it.  We ensure that the next
|right_seg| that we select will not overlap by noting the left-most extent of
the segment we've just put on the |out_list|.

@<Transfer right-most segment to the |out_list|@>=
if right_seg <> nil then
begin
  @<Remove |right_seg| from list@> ;
  right_seg^.next:=out_list;            {Put rightmost at start of |out_list|}
  out_list:=right_seg;
  x_left:=right_seg^.xs         {Don't consider anything right of its LHS}
end

@  If |left_seg| is |nil| then |right_seg| is the first segment associated
with the line, and so we remove it from the front of the list.  Otherwise, we
detach it from the middle of the list.

@<Remove |right_seg| from list@>=
if left_seg = nil then
  seg_list[k]:=seg_list[k]^.next
else
  left_seg^.next:=left_seg^.next^.next

@ Once we've constructed the list out of as many segments as possible for the
current ``line'', we can output them all and go back for some more.

@<Output list of non-overlapping segments@>=
while out_list <> nil do
begin
  ln3_cnt:=0; ln3_print:=0;     {Nothing as yet on line}
  esc_out(csi+str_int(k)+'d');  {Move to correct line on paper}
  cur_seg:=out_list; prev_seg:=nil;
  still_fits:=true;
  @<Output current segment, in chunks if necessary@> ;
  @<Return processed segments to the |free_seg_list|@>;
  write_ln(ln3_file)
end

@ Once we have a single segment ready for output, we call |print_seg| to
output as much of it as will fit in the space remaining in the output buffer.
If this is the whole segment, we can then repeat the process with the next
segment in the |out_list|, noting the address of this segment so that it may
be returned to the |free_seg_list|.  On the other hand, if |print_seg| decided
to split the segment, we break out of the loop.

@<Output current segment, in chunks if necessary@>=
while (cur_seg <> nil) and still_fits do
begin right_seg:=cur_seg;
  print_seg;            {Will leave |cur_seg=nil| if all text output}
  if cur_seg = nil then
  begin
    cur_seg:=right_seg^.next;   {Next segment for output}
    prev_seg:=right_seg
  end else
    still_fits:=false;
end;

@ Once a segment (or group of segments) have been output, it is safe to
discard them.  We do this by returning them to the |free_seg_list|, so that
new nodes will be allocated therefrom in preference to using |new|.  The first
such freed segment is that at the begining of the |out_list|, whilst the last
is that most recently output, noted in |prev_seg|.

@<Return processed segments to the |free_seg_list|@>=
if prev_seg <> nil then
begin prev_seg^.next:=free_seg_list;
  free_seg_list:=out_list;
  out_list:=cur_seg
end

@ When \.{DVItoLN03} first starts, any output produced will be printed on the
first page.  Subsequently, it is necessary to insert a form-feed character to
terminate the preceding page before any output for another page may commence.
The |boolean| |top_of_page| is used to keep track of whether the form-feed is
required.

This code is called whenever something is about to be output to the page: if
the orientation is incorrect at that time, the appropriate change is made.
It's also the appropriate time to change paper trays, and select the
printing mode.

@<Move to top of form@>=
if top_of_page then
begin
  top_of_page:=false;
  if pages_printed>0 then write_ln(ln3_file,FF);
  @<Change tray for second sheet, if requested@>;
  @<Skip to a \\{recto} page, if necessary@>
end;
@<Change page orientation@>

@ With the DEClaser~2200, it is possible (through the \.{FIRST} and \.{REST}
selectors of the \.{/FEED\_TRAY} qualifier) to arrange for the second and
subsequent sheets to be printed on paper from a different input tray than
that used for the first sheet.  The code in this module effects this tray
change, when the second sheet to be printed is first met: it  firstly
determines whether we're printing a \\{recto} page, when duplexing (this
information is later used for selecting the printing mode for the current
page).  The ``cascade'' of |if| clauses is to reduce the processing overhead
except for the first three pages printed.

The macro here is used to change trays; the control sequence ending in
|'!v'| is the |DECASTC| (Automatic Sheet-feeder Tray Control) command, to
select the desired tray.

@d select_tray(#)==case # of
  default_tray: do_nothing;
  othercases
    write(ln3_file,csi,#:1,'!v')
endcases

@d odd_page==(odd(count[0])or(count[0]=0))

@d recto_page==(duplex_by_page_numbers and odd_page) or
  (not duplex_by_page_numbers and not odd(pages_printed))

@d starting_second_sheet==
  (((print_mode<true_duplex_normal)or(print_mode>true_duplex_tumbled)) and
   (pages_printed=1)) {non-duplex; change at second page}
  or
  (((print_mode=true_duplex_normal)or(print_mode=true_duplex_tumbled)) and
   this_page_recto) {duplex; change at second sheet}

@<Change tray for second sheet, if requested@>=
this_page_recto:=recto_page;
if pages_printed<=2 then
  if pages_printed>0 then
    if starting_second_sheet then
      if following_tray<>first_tray then
        select_tray(following_tray)

@ When we are printing in duplex mode (even simulated duplex, in response to
the \.{MASTER} option of the \.{/PRINT\_MODE} qualifier), we have to arrange
to leave a page blank if two \\{recto} or two \\{verso} pages follow each
other.  This information will already have been put into |this_page_recto|,
when the paper tray was selected.

For efficiency reasons, when printing in a duplex mode on the DEClaser~2200
(LN06), we switch into simplex mode for a \\{recto} page which is followed
by another such: this information was recorded in the array |blank_follows|
when the starting page was being located.  This speeds up processing,
because the printer doesn't need to perform the mechanical reversal of the
page only to find that is has nothing to be printed upon it.

@<Skip to a \\{recto} page, if necessary@>=
@<Reset duplexing, if appropriate@>;
if print_mode>=true_duplex_normal then
begin
  if last_page_recto=this_page_recto then
  begin
    if (print_mode=ln03_master) or not this_page_recto then
      write(ln3_file,' ',FF)  {Eject an extra page on LN03, or make
                               \\{recto} page blank}
  end else
  if (print_mode<ln03_master) and (ptr_blanks>0) and
      duplex_by_page_numbers and this_page_recto then
    if blank_follows[ptr_blanks]=cur_loc_after_bop then
    begin {This page has blank \\{verso} side}
      write(ln3_file,csi,true_simplex_normal:1,' x');
      duplex_to_be_reset:=true;
      decr(ptr_blanks)
    end
end;
last_page_recto:=this_page_recto;

@ If the previous page was printed in simplex mode, because it was a
\\{recto} page to be followed by another one then when we meet that next
\\{recto} page we have a look to see if the following page is yet another
\\{recto} one.  If not, we have to reset the required duplexing mode.

@<Reset duplexing, if appropriate@>=
if duplex_to_be_reset then {previous page was temporary excursion into
                            simplex mode}
  if ptr_blanks>0 then
    if blank_follows[ptr_blanks]=cur_loc_after_bop then
    begin
      decr(ptr_blanks); {and remain in simplex mode}
      if print_mode>=duplex_master_normal then
        write(ln3_file,' ',FF) {eject blank page for \\{verso} master}
    end else
    begin
      write(ln3_file,csi,print_mode:1,' x');
      duplex_to_be_reset:=false
    end

@ When we come to the end of the document when printing in any duplexing
mode, we may just have printed a \\{recto} page; we must either eject the
second side as blank (in true duplex modes) or print an extra blank page (in
any of the master modes).  This is accomplished by printing a form-feed, and
following it with a space; the form-feed on its own would just result in the
printer symbiont suppressing that which it would have inserted, but the
space fools it into sending a second form-feed itself.

@<Conditionally eject last page@>=
if (print_mode>=true_duplex_normal) and last_page_recto then
  if (print_mode>=duplex_master_normal) or not duplex_to_be_reset then
    write(ln3_file,FF,' ')

@ Here are the variables required to control \\{recto}/\\{verso} printing

@<Glob...@>=
@!last_page_recto:boolean; {|true| if previous page was \\{recto}}
@!this_page_recto:boolean; {used to save multiple evaluation of |recto_page|}
@!pages_printed:integer;   {counts each page generated for
                                \.{/noduplex\_by\_page\_numbers}}
@!duplex_to_be_reset:boolean; {Used to reset any duplex mode after one sheet
                               has been printed in simplex}
@!cur_loc_after_bop:integer;  {Records |cur_loc| after the current page's
                               |bop| command was processed}
                        
@ These variables have to be initialized, of course

@<Set initial...@>==
pages_printed:=0; last_page_recto:=false; duplex_to_be_reset:=false;

@ When we first start writing the output file, we have to add commands to
tell the DEClaser printers from which tray to take the first sheet of paper,
and which printing mode to use.  The control sequence ending in |' x'| is
the |DECSPDM| (Set Duplex Print Mode) command.

@<Select printing mode and feed tray on DEClaser printers@>=
if first_tray<>default_tray then
  select_tray(first_tray);
if (print_mode>default_print_mode) and (print_mode<ln03_master) then
  write_ln(ln3_file,csi,print_mode:1,' x');

@*Procedures for recognizing and manipulating strings.  We have a
requirement for recognizing various ASCII strings in this program.  Some of
these strings specify functionality required from a \.{\BS special} command,
whilst others are the two-letter abbreviations used to specify the units of
dimensions: such dimensions can appear within \.{\BS special} commands and
also in some command line qualifiers.

The following data structures support the recognition of the strings used:
for commands, we need the following texts:

\yskip\hang\.{sx\ } ``Old-style'' introducer for inclusion of an LN03 sixel
file within the program's output.  Note that support for this command will
be removed in a future release following V4.0.

\yskip\hang\.{landscape} Output the following information in landscape orientation.

\yskip\hang\.{portrait} Output the following information in portrait orientation.

\yskip\hang\.{ln03:} New-style introducer for all LN03-specific \.{\BS
special}s.  All the remaining command strings are to be found following this
introducer.

\yskip\hang\.{sixel\ } Command to include a pixel file into the program's
output.

\yskip\hang\.{plotfile\ } Alternative name to include a sixel file.

\yskip\hang\.{defpoint\ } Define the coordinates of a point on the page:
used in conjunction with \.{connect} to draw changebars.

\yskip\hang\.{connect\ } Draw a changebar line between a pair of previously
defined points.

\yskip\hang\.{resetpoints\ } Clear all recorded details for a specified
range of named points.

\noindent Dimensions recognized are as in \TeX, with the addition of \.{px}
(representing a dimension unit of one pixel) and \.{mi} (representing one
micrometre or micron).

The creation of these strings could have been much cleaner through use of
{W\sc EB}'s string pool feature, but it was felt that it was nevertheless
better to avoid the use of the auxiliary \.{.POOL} file.

@<Types...@>=
@!pool_pointer = 0..pool_size;
@!str_number = 0..max_strings;

@ @<Globals...@>=
@!str_pool : packed array [pool_pointer] of ASCII_code;
@!str_start : array [str_number] of pool_pointer;
@!pool_ptr : pool_pointer; {First unused position in |str_pool|}
@!str_ptr : str_number;   {Number of current string being created}

@ Here's how we initialize all the strings for recognition.  Note that
string $0$ is reserved for the null string.  The macro definitions allow us
to refer to the various strings symbolically; note that the related numbers
will need to be changed if the order of initialization below is altered!
|min_string_match| specifies the minimum number of characters which are
required to match when the string being compared has been specified in
truncated form; at present, all strings are unique in their first two
characters.

@d min_string_match=2
@#
@d dimen_pt=1
@d dimen_mm=dimen_pt+1
@d dimen_in=dimen_mm+1
@d dimen_px=dimen_in+1
@d dimen_cm=dimen_px+1
@d dimen_pc=dimen_cm+1
@d dimen_bp=dimen_pc+1
@d dimen_dd=dimen_bp+1
@d dimen_cc=dimen_dd+1
@d dimen_sp=dimen_cc+1
@d dimen_mi=dimen_sp+1
@d spec_sx=dimen_mi+1
@d spec_land=spec_sx+1
@d spec_port=spec_land+1
@d spec_ln=spec_port+1
@d spec_six=spec_ln+1
@d spec_plt=spec_six+1
@d spec_set=spec_plt+1
@d spec_con=spec_set+1
@d spec_rst=spec_con+1

@<Set initial...@>=
pool_ptr := 0; str_ptr := 0; str_start[0] := 0;
make_string('pt');
make_string('mm');
make_string('in');
make_string('px');
make_string('cm');
make_string('pc');
make_string('bp');
make_string('dd');
make_string('cc');
make_string('sp');
make_string('mi');
make_string('sx ');
make_string('landscape');
make_string('portrait');
make_string('ln03:');
make_string('sixel ');
make_string('plotfile ');
make_string('defpoint ');
make_string('connect ');
make_string('resetpoints ');
incr(str_ptr); str_start[str_ptr]:=pool_ptr;

@ Here's the procedure for putting the strings into the pool.  We make use
here  of a couple of auxiliary macros for adding characters to the string
pool, and another for reporting errors with the arguments of a \.{\BS
special}.

@d print_spec == for spec_ptr:= 1 to spec_len-1 do
                 begin print(xchr[spec_par[spec_ptr]]);
                       write(term_out,xchr[spec_par[spec_ptr]])
                 end;
                 print(''''); write(term_out,'''')
@d spec_error(#) == begin error(#,' `'); print_spec end

@d append_char(#) == {Put |ASCII_code| \# at the end of the |str_pool|}
  begin
    str_pool[pool_ptr] := #;
    incr(pool_ptr)
  end
@d str_room(#) == {make sure that |str_pool| can take a string of length \#}
  begin
    if pool_ptr + # > pool_size then
      abort('Internal error (String pool overflow)')
@:fatal error Internal error String pool overflow}{\qquad\.{(String pool overflow)}@>
  end

@<Procedures for...@> =
procedure make_string(@!s : packed array [@!low..@!high : integer] of char);
var
  @!i : integer;
begin
  str_room(high-low+1);
  incr(str_ptr);
  str_start[str_ptr] := pool_ptr;
  for i := low to high do append_char(xord[s[i]]);
end;

@ This module has a set of functions (and one procedure) in it that are
generally used in decoding \.{\BS special} commands.  All these procedures,
together with those in the following two sections, are ``low-level'' and
included at an early point in the \PASCAL\ so that they are available to all
routines provided through {W\mc EB}'s \.{@@p} directive.

The functions are:
\item{$\bullet$} |rd_char|, which reads a character from |spec_par| at
|spec_ptr|,
\item{$\bullet$} |rd_nb_char|, which reads a non-blank character, using
|rd_char|,
\item{$\bullet$} |rd_int|, which reads an integer, using the two |rd_char|s,
\item{$\bullet$} |rd_real|, which reads a `real' number, but without exponent.

All the functions return a |boolean|, which is |true| if they `failed' (which,
in the case of the |rd_char|s, means they got to the end of the line).
|rd_real| accepts |'.'| as `0.0' (!). (Personally, I wouldn't give you twopence
for these number routines: they have several curious properties, including the
pleasing way they collapse when presented with an isolated minus-sign!)
[This comment was made by Robin Fairbairns
@^Fairbairns, Robin@>
of LaserScan (Cambridge UK), who wrote them and provided the other features
that support \.{\\changebar} commands through appropriate \.{\\special}
strings.]

The one procedure in the module is |bksp_char|, which backspaces by one
character, except, of course, at the beginning of the buffer.

@<Additional low...@>=
function rd_char (var ch : [volatile] ASCII_code) : boolean;
begin
if spec_ptr>spec_len
then begin ch:=" "; rd_char:=true; end
else begin ch:=spec_par[spec_ptr]; incr(spec_ptr); rd_char:=false; end;
end;

function rd_nb_char (var ch : [volatile] ASCII_code) : boolean;
var
@! end_of_line : boolean;
begin
repeat end_of_line := rd_char(ch) until end_of_line or (ch<>" ");
rd_nb_char := end_of_line;
end;

procedure bksp_char;
begin
if spec_ptr>1 then decr(spec_ptr);
end;

function rd_int ( var res : [volatile] integer ) : boolean;
var
@!ch : ASCII_code;
@!end_of_line,negative : boolean;
begin
res := 0; negative := false;
end_of_line:=rd_nb_char(ch);
if ch="-" then begin
  negative := true;
  end_of_line := rd_char(ch);
  end;
if end_of_line then rd_int:=true else rd_int:=not (ch in ["0".."9"]);
while (not end_of_line) and (ch in ["0".."9"]) do begin
  res:=res*10+(ch-"0");
  end_of_line:=rd_char(ch);
  end;
if not end_of_line then bksp_char;
if negative then res:=-res;
end;

function rd_real (var res : [volatile] real ) : boolean;
var
@!ch : ASCII_code;
@!end_of_line,negative:boolean;
@!in_fract:boolean;
@!fract:real;
@!dec_pt:real;

begin
res:=0.0; fract:=0.0; dec_pt:=1.0; negative := false;
end_of_line:=rd_nb_char(ch);
if ch="-" then begin
  negative := true;
  end_of_line := rd_char(ch);
  end;
if end_of_line then rd_real:=true
else begin
  rd_real:=false; {initial estimate}
  if ch in ["0".."9"]
  then in_fract:=false
  else if ch="."
  then begin
    in_fract:=true;
    end_of_line:=rd_char(ch);
    end
  else rd_real:=true;
  end;
while (not end_of_line) and (ch in ["0".."9"])
do begin
  if in_fract then begin
    dec_pt:=dec_pt*0.1;
    fract:=fract+dec_pt*(ch-"0");
    end
  else res:=res*10.0+(ch-"0");
  end_of_line:=rd_char(ch);
  if not(end_of_line or in_fract) then
    if ch="." then begin
      in_fract:=true;
      end_of_line:=rd_char(ch);
      end;
  end;
if not end_of_line then bksp_char;
if negative then res:=-res-fract else res:=res+fract;
end;

@ Here is a function which matches characters from the buffer into which
the string associated with the \.{\BS special} command is read against those
strings in the |str_pool|.  It returns the index number of that particular
string.  It is not necessary for the whole stored string to be matched; it
will be noted that all strings are unique in their first two characters;
provided at least these characters are matched \&{and} the first non-matched
character in the \.{\BS special} command is a space character, then a match
will still be found. The function returns a pointer to string $0$ (the null
string) if no match is found.

Here is a macro which allows us to find the length of a string in the pool:

@d str_length(#) == (str_start[#+1]-str_start[#])

@<Additional low...@>=
function find_match : str_number;
var
  @!i,@!l : integer;
  @!m : str_number;
  @!chr_ptr : pool_pointer;
  @!eol,@!found : boolean;
begin
  eol := false;  {We start by skipping over any leading spaces}
  while (spec_par[spec_ptr] = " ") and not eol do
    if spec_ptr >= spec_len then
      eol := true
    else
      incr(spec_ptr);
  m := 0;    {Assume the worst!}
  if eol then spec_error('Unsupported or malformed \special ')
@:Error: Unsupported or malformed special}{\quad\.{Unsupported or malformed \\special}@>
  else
  begin
    i := spec_ptr;  {Point into the body of the \.{\BS special} command}
    m := dimen_pt;
    found := false;
    repeat
      l := str_length(m);
      chr_ptr := str_start[m];
      while ((spec_par[i] = str_pool[chr_ptr]) or
            (spec_par[i] = str_pool[chr_ptr]+"A"-"a")) and
            not eol and not found do
      begin
        decr(l);
        if l=0 then found := true else
        begin
          if spec_ptr >= spec_len then eol := true
          else
            incr(i);
          incr(chr_ptr);
        end;
      end;
      if eol or (spec_par[i] = " ") then
        if i-spec_ptr > min_string_match then found := true;
      if not found then
      begin
        i := spec_ptr; incr(m);
      end
    until found or (m > str_ptr);
    if not found then m := 0
    else spec_ptr:=i+1;
  end;
  find_match := m;
end;

@ This module defines a `derived' function for reading, as an integer number of
pixels, a `dimension' from the \.{\BS special}'s arguments (where `dimension'
is---roughly---what one gets in \TeX).  It firstly reads a \meta{real number}
from the argument string, and then multiplies this by an appropriate factor
determined by the following two character string, which should be one of \TeX's
\meta{physical unit} designators (\.{pt}, \.{bp}, etc) with the addition of
\.{px}, denoting pixels, and \.{mi} for microns ($\mu\hbox{m}$).

The function has a |boolean| result, which once again is |true| if it fails to
find what was expected.

@d spec_warning(#)==
    begin
      warning(#,' `'); print_spec
    end

@<Additional low...@>=
function rd_dimension ( var res : [volatile] integer ) : boolean;
var
@! dimen_found : str_number;
@! real_value : real;
begin
if rd_real(real_value) then rd_dimension:=true
else begin
  rd_dimension:=false;
  dimen_found := find_match;
  case dimen_found of
    dimen_pt: real_value:=real_value*resolution/72.27;
    dimen_in: real_value:=real_value*resolution;
    dimen_px: do_nothing;
    dimen_pc: real_value:=real_value*12.0*resolution/72.27;
    dimen_cm: real_value:=real_value*resolution/2.54;
    dimen_mm: real_value:=real_value*resolution/25.4;
    dimen_bp: real_value:=real_value*resolution/72.0;
    dimen_dd: real_value:=real_value*(1238.0/1157.0)*resolution/72.27;
    dimen_cc: real_value:=real_value*(14856.0/1157.0)*resolution/72.27;
    dimen_mi: real_value:=real_value*resolution/25400.0;
    dimen_sp: real_value:=real_value*resolution/65536.0/72.27;
    othercases
      begin
        spec_warning('Missing or unknown unit of measure:');
@:Warning: Missing or unknown unit of measure}{\quad\.{Missing or unknown unit of measure}@>
        rd_dimension:=true; {Assumes value is in pixels}
      end;
  endcases;
end;
res := round(real_value);
end;

@ This analogous function reads a dimension and converts it to scaled
points; this is used by the \.{/hfuzz} and \.{/vfuzz} qualifiers.

@<Additional low...@>=
function rd_scaled_pt ( var res : [volatile] integer ) : boolean;
var
@! dimen_found : str_number;
@! real_value : real;
begin
if rd_real(real_value) then rd_scaled_pt:=true
else begin
  rd_scaled_pt:=false;
  dimen_found := find_match;
  case dimen_found of
    dimen_pt: real_value:=real_value*65536.0;
    dimen_in: real_value:=real_value*65536.0*72.27;
    dimen_px: real_value:=real_value/conv;
    dimen_pc: real_value:=real_value*12.0*65536.0;
    dimen_cm: real_value:=real_value*65536.0*72.27/2.54;
    dimen_mm: real_value:=real_value*65536.0*72.27/25.4;
    dimen_bp: real_value:=real_value*65536.0*72.27/72.0;
    dimen_dd: real_value:=real_value*(1238.0/1157.0)*65536.0;
    dimen_cc: real_value:=real_value*(14856.0/1157.0)*65536.0;
    dimen_mi: real_value:=real_value*65536.0*72.27/25400.0;
    dimen_sp: do_nothing;
    othercases
      begin
        spec_warning('Missing or unknown unit of measure:');
@:Warning: Missing or unknown unit of measure}{\quad\.{Missing or unknown unit of measure}@>
        rd_scaled_pt:=true; {Assumes value is in sp}
      end;
  endcases;
end;
res := round(real_value);
end;

@ Collection of rules for a page is fairly trivial, but it's now called from
three places, so we make it a procedure. All rules are collected and placed
into |page_rules|. Later, the rules will be merged.

@p procedure Collect_rule(hhh, vvv, wwidth, hheight : integer);
begin
if rules_on_page<max_rules then incr(rules_on_page) else
if not rule_limit_exceeded then begin
warning('more than ',max_rules:1,' rules on page; excess ignored!');
@.more than ... rules@>
rule_limit_exceeded:=true end;
with page_rules[rules_on_page] do begin
        horiz := hhh; vert := vvv; height := hheight;
        width := wwidth; covered := false;
          end;
end;

@ This module defines three procedures:

\yskip\hang$\bullet$ |reset_points|, which resets the |present| flags of a
range of |point|s,

\yskip\hang$\bullet$ |define_point|, which gives a value to a |point|, and

\yskip\hang$\bullet$ |connect_points|, which makes connection between two
|point|s.

The macro simplifies the task of finding the next comma in the \.{\BS
special} string.

@d find_comma ==   repeat end_of_line:=rd_nb_char(ch)
  until end_of_line or (ch<>",")

@p procedure reset_points;
var
@!low,high : integer;
begin
rd_int(low); rd_int(high);
if (0<low) and (low<=high) and (high<=max_points)
then begin
  for k:=low to high do point[k].present:=false;
  end
else if (0<low) and (low<=max_points)
then point[low].present:=false;
end;

procedure define_point;
var
@! which : integer; {the point number}
@! h_val, v_val : integer; {its coordinates}
@! ch : ASCII_code;
@! end_of_line : boolean;
begin
if rd_int(which) then
  spec_error('Point number missing in \special ')
@:Error: Point number missing in special}{\quad\.{Point number missing in \\special}@>
else if (which<=0) or (which>max_points) then
  spec_error('Invalid point number in \special ')
@:Error: Invalid point number in special}{\quad\.{Invalid point number in \\special}@>
else begin
  find_comma;
  if ch="("
  then begin
    if rd_dimension(h_val)
    then begin
      h_val:=hh;
      v_val:=vv;
      end
    else begin
      if rd_nb_char(ch) then do_nothing;
      if ch=","
      then begin
        if rd_dimension(v_val) then v_val:=vv;
        end
      else v_val:=vv;
      end;
    end
  else
    begin
    h_val:=hh; v_val:=vv;
    end;
  {now we've got |h_val|, |v_val| and |which| all set (to something!)}
  with point[which] do
  begin hh := h_val; vv := v_val; present := true end;
  end;
end;

procedure connect_points;
var
@! a1,b1, a2,b2 : integer;
@! width : integer;
@! half_width : integer;
@! end_of_line, bad_point : boolean;
@! ch : ASCII_code;
begin
end_of_line:=rd_int(a1);
end_of_line:=rd_nb_char(ch);
if ch="/"
then end_of_line:=rd_int(b1)
else begin
  if not end_of_line then bksp_char; b1:=a1;
  end;
find_comma;
if not end_of_line then bksp_char;
end_of_line:=rd_int(a2);
end_of_line:=rd_nb_char(ch);
if ch="/"
then end_of_line:=rd_int(b2)
else begin
  if not end_of_line then bksp_char; b2:=a2;
  end;

find_comma;
if not end_of_line then bksp_char;
if rd_dimension(width) then width:=2; {default dimension is 2 pixels, ~0.4 pts}
if odd(count[0])
then begin b1:=a1; b2:=a2; end;

{check the point numbers}
if (b1<=0) or (b1>max_points) or (b2<=0) or (b2>max_points)
then bad_point:=true
else bad_point:=false;
if not bad_point then begin
  if (not point[b1].present) or (not point[b2].present)
  then bad_point:=true;
  end;
if bad_point then
  spec_error('Invalid point number in \special ')
@:Error: Invalid point number in special}{\quad\.{Invalid point number...}@>
else begin
  half_width := width div 2;
  if point[b1].hh=point[b2].hh
  then Collect_rule(point[b1].hh-half_width,
                    VAX_max(point[b1].vv,point[b2].vv),
                    width,
                    abs(point[b2].vv-point[b1].vv))
  else if point[b1].vv=point[b2].vv
  then Collect_rule(VAX_min(point[b1].hh,point[b2].hh),
                    point[b1].vv+half_width,
                    abs(point[b1].hh-point[b2].hh),
                    width)
  else spec_error('Diagonal rule in \special ');
@:Error: Diagonal rule in special}{\quad\.{Diagonal rule in \\special}@>
  end;
end;


@* Translation to symbolic form.
The main work of \.{DVItoLN03} is accomplished by the |do_page| procedure,
which produces the output for an entire page, assuming that the |bop|
command for that page has already been processed. This procedure is
essentially an interpretive routine that reads and acts on the \.{DVI}
commands.

@ The definition of \.{DVI} files refers to six registers,
$(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units.  In practice,
we also need registers |hh| and |vv|, the pixel analogs of $h$ and $v$,
since it is not always true that |hh=pixel_round(h)| or
|vv=pixel_round(v)|.  Note that the current font is neither stacked
nor restored by |push| and |pop| commands.

The stack of $(h,v,w,x,y,z)$ values is represented by eight arrays
called |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|.

@<Glob...@>=
@!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values}
@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack:
  array [0..stack_size] of integer; {pushed down values in \.{DVI} units}
@!hhstack,@!vvstack:
  array [0..stack_size] of integer; {pushed down values in pixels}

@ Three characteristics of the pages (their |max_v|, |max_h|, and
|max_s|) are specified in the postamble, and a warning message
is printed if these limits are exceeded. Actually |max_v| is set to
the maximum height plus depth of a page, and |max_h| to the maximum width,
for purposes of page layout. Since characters can legally be set outside
of the page boundaries, it is not an error when |max_v| or |max_h| is
exceeded. But |max_s| should not be exceeded.

The postamble also specifies the total number of pages; \.{DVItoLN03}
checks to see if this total is accurate.

@<Glob...@>=
@!max_v:integer; {the value of |abs(v)| should probably not exceed this}
@!max_h:integer; {the value of |abs(h)| should probably not exceed this}
@!max_h_saved,@!max_v_saved:integer; {`Static' value for oversize messages}
@!max_s:integer; {the stack depth should not exceed this}
@!max_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels}
@!total_pages:integer; {the stated total number of pages}
@!page_count:integer; {the total number of pages seen so far}

@ @<Set init...@>=
max_v:=@'17777777777-99; max_h:=@'17777777777-99; max_s:=stack_size+1;@/
max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0;

@ Here is a procedure which will be required when decoding |xxx| commands.
If the string of characters in a \TeX\ \.{\\special} command commences with
the character string |'SX '| (i.e., `old style'), or |'ln03:plotfi[le] '| or
|'ln03:sixel '| (`new style'), the following procedure will  copy the
remainder of the \.{\\special} command's parameter string (which is assumed
to be the name of a VAX/VMS file containing a graphics image encoded into
DEC's standard ``sixel'' format) into the |text| field of a |segment|; the
remaining fields are used to record the page co\"ordinates at which the dump
shall be placed.

@p procedure include_sixel_dump;
begin
  finish_seg; {ensure that we have no |cur_seg|}
  new_segment(true,true,0,0); {|font_code| and |ch_code| irrelevant}
  with cur_seg^ do
  begin
    seg_type:=sixel_dump;   {that's the flavour of segment}
    text:='';
    while spec_ptr <= spec_len do
    begin
      text := text + xchr[spec_par[spec_ptr]];
      incr(spec_ptr)
    end;
    xs:=x_pos; {record starting position of dump's placement}
  end;
  finish_seg {detach the segment; it's attached to correct |seg_list|}
end;

@ Before we get into the details of |do_page|, it is convenient to
consider a simpler routine that computes the first parameter of each
opcode.

@p function first_par(o:eight_bits):integer;
begin case o of
sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
  first_par:=o-set_char_0;
set1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte;
set1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes;
set1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes;
right1,w1,x1,down1,y1,z1: first_par:=signed_byte;
right1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair;
right1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio;
set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3,
  fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad;
nop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0;
w0: first_par:=w;
x0: first_par:=x;
y0: first_par:=y;
z0: first_par:=z;
sixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0;
end;
end;

@ Here is another subroutine that we need: It computes the number of
pixels in the height or width of a rule. Characters and rules will line up
properly if the sizes are computed precisely as specified here.  (Since
|conv| is computed with some floating-point roundoff error, in a
machine-dependent way, format designers who are tailoring something for a
particular resolution should not plan their measurements to come out to an
exact integer number of pixels; they should compute things so that the
rule dimensions are a little less than an integer number of pixels, e.g.,
4.99 instead of 5.00.)

@p function rule_pixels(x:integer):integer;
  {computes $\lceil|conv|\cdot x\rceil$}
var n:integer;
begin n:=trunc(conv*x);
if n<conv*x then rule_pixels:=n+1 @+ else rule_pixels:=n;
end;

@ Strictly speaking, the |do_page| procedure is really a function with
side effects, not a `\&{procedure}'\thinspace; it returns the value |false|
if \.{DVItoLN03} should be aborted because of some unusual happening. The
subroutine is organized as a typical interpreter, with a multiway branch
on the command code followed by |goto| statements leading to routines that
finish up the activities common to different commands. We will use the
following labels:

@d fin_set=41 {label for commands that set or put a character}
@d fin_rule=42 {label for commands that set or put a rule}
@d move_right=43 {label for commands that change |h|}
@d move_down=44 {label for commands that change |v|}
@d change_font=45 {label for commands that change |cur_font|}

@ Some \PASCAL\ compilers severely restrict the length of procedure bodies,
so we shall split |do_page| into two parts, one of which is
called |special_cases|. The different parts communicate with each other
via the global variables mentioned above, together with the following ones:

@<Glob...@>=
@!s:integer; {current stack size}
@!cur_font:integer; {current internal font number}
@!cur_base:integer; {$\equiv$|glyph_base[cur_font]|, to index into |glyphs|}

@ Here is the overall setup.  The parameter is an extension from the
original program to accommodate virtual font character sequences; in normal
operation (when processing bytes from the \.{DVI} file), this parameter is
negative, and |do_page| processes one complete page of the file.  If a
character being set on the current page is taken from a virtual font, and
doesn't simply map to a single physical character from some other font, then
|do_page| will be called recursively, with a parameter that points to the
first byte of a sequence of |dvi| bytes which implements the virtual
character.

@p @t\4@>@<Declare the function called |special_cases|@>@;
@t\4@>@<Declare the function called |reduce_rules|@>@;
function do_page; {|do_page(@!vf_start:integer):boolean;|}
label fin_set,fin_rule,move_right,done,9998,9999;
var o:eight_bits; {operation code of the current command}
@!p,@!q:integer; {parameters of the current command}
@!a:integer; {byte number of the current command}
@!hhh:integer; {|h|, rounded to the nearest pixel}
@!saved_font:integer; {implicit |push| around virtual font character}
@!old_take:integer; {ditto for taking |dvi| from array |vf|}
begin
  if vf_start < 0 then
  begin {initialize the state variables if not recursing}
    s:=0; h:=0; v:=0; w:=0; x:=0; y:=0; z:=0; hh:=0; vv:=0;
  end;
  saved_font:=cur_font;  {for later restoration}
  cur_font:=nf; {set current font undefined}
  old_take:=vf_take;
  vf_take:=vf_start; {ensure |dvi| bytes taken from appropriate source}
while true do @<Translate the next command in the \.{DVI} file;
    |goto 9999| with |do_page=true| if it was |eop|;
    |goto 9998| if premature termination is needed@>;
9998: print_ln('!'); do_page:=false;
9999: cur_font:=saved_font;
cur_base:=glyph_base[cur_font];
vf_take:=old_take;
end;

@ Error messages are written to the log file with full details of their position
within the \.{DVI} file, and, in a shorter form, to the terminal.


@<Translate the next command...@>=
begin a:=cur_loc;
o:=get_byte; p:=first_par(o);
if eof(dvi_file) then bad_dvi('the file ended prematurely');
@:Bad DVI file the file ended prematurely}{\quad\.{the file ended prematurely}@>
@<Start translation of command |o| and |goto| the appropriate label to
  finish the job@>;
fin_set: @<Finish a command that either sets or puts a character, then
    |goto move_right| or |done|@>;
fin_rule: @<Finish a command that either sets or puts a rule, then
    |goto move_right| or |done|@>;
move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>;
done:
end

@ The multiway switch in |first_par|, above, was organized by the length
of each command; the one in |do_page| is organized by the semantics.

@<Start translation...@>=
if o<set_char_0+128 then @<Translate a |set_char| command@>
else case o of
  four_cases(set1): goto fin_set;
  four_cases(put1): goto fin_set;
  set_rule: goto fin_rule;
  put_rule: goto fin_rule;
  @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
  @t\4@>@<Cases for horizontal motion@>@;
  othercases if special_cases(o,p,a) then goto done@+else goto 9998
  endcases

@* Rule Accumulation.
We accumulate all rules generated on a page. At the end of the page,
the rules are combined to form larger rules when possible, then
output.

A special record describes the rule rectangles encountered.
@<Types...@>=
@!rules = record {rule record}
@!horiz,@; {Horizontal Coordinate}
@!width,@; {Width in Horizontal Direction}
@!vert,@; {Vertical Coordinate}
@!height:integer; {Height in Vertical Direction}
@!covered:boolean; {Indicates if combined}
end;

@ An array of |rules| is defined along with an index.
@<Glob...@>=
@!page_rules:array[1..max_rules] of rules; {rules generated on this page}
@!rules_on_page: 0..max_rules; {count of rules found on this page}
@!rule_limit_exceeded: boolean; {used to produce just one warning message}

@ Initialize |rules_on_page| to show no rules yet.
@<Set init...@>=
@!rules_on_page := 0; {set no rules found yet}
@!rule_limit_exceeded := false; {so we haven't got too many, either!}

@ This module used to contain the code for collecting rules, but now it's
limited to calling a procedure elsewhere that does the job.

@<Collect Rules for Page@>=
Collect_rule(hh, vv, rule_pixels(q), rule_pixels(p));

@ Rules can now be combined and output in the \.{LN3} file. To accomplish
this the rules are scanned in vertical and then in horizontal order. During
the scan adjacent rules are combined and marked as used. At completion, rules
that aren't marked as |covered| are output.

@<Combine and output Rules@>=
if rules_on_page > 0 then
              begin
              reduce_rules;
              for rules_on_page := rules_on_page downto 1 do
                with page_rules[rules_on_page] do
                  if not covered then
                    write_ln(ln3_file,csi,'1;',horiz+left_marg:1,';',
                                vert-height+top_marg:1,';',
                                height:1,';',width:1,'!|');
              rules_on_page := 0
              end;

@ |reduce_rules| implements the rule reduction algorithm. Maps are generated
for traversing all rules both horizontally and vertically. Rules can overlap
or underlap by up to |max_lap_rules| in |height| or |width|.

@<Declare the function called |reduce_rules|@>=
procedure reduce_rules;
var
horiz_map: array[1..max_rules] of 0..max_rules;
verti_map: array[1..max_rules] of 0..max_rules;
hi,hj,vi,vj: 0..max_rules;
ht,vt:integer;
begin
for hi := 1 to rules_on_page do horiz_map[hi] := hi;
for vi := 1 to rules_on_page do verti_map[vi] := vi;
@<Build Horizontal Map@>@;
@<Traverse Horizontally@>@;
@<Build Vertical Map@>@;
@<Traverse Vertically@>@;
end;

@ Build map for horizontal combination by sorting the rules into
increasing vertical order and within the same vertical line into
increasing horizontal order. This allows for rule combination by
looking for |width| overlap.
@<Build Horiz...@>=
for hi := 1 to rules_on_page-1 do
 for hj := (hi+1) to rules_on_page do
  if (page_rules[horiz_map[hi]].vert > page_rules[horiz_map[hj]].vert) or
     ((page_rules[horiz_map[hi]].vert = page_rules[horiz_map[hj]].vert) and
      (page_rules[horiz_map[hi]].horiz > page_rules[horiz_map[hj]].horiz)) then
        begin ht:=horiz_map[hi]; horiz_map[hi]:=horiz_map[hj];
              horiz_map[hj]:=ht end;

@ Build map for vertical combination by sorting the rules into
increasing horizontal order and within the same horizontal line into
increasing vertical order. This allows for rule combination by
looking for |height| overlap.
@<Build Verti...@>=
for vi := 1 to rules_on_page-1 do
 for vj := (vi+1) to rules_on_page do
  if (page_rules[verti_map[vi]].horiz > page_rules[verti_map[vj]].horiz) or
     ((page_rules[verti_map[vi]].horiz = page_rules[verti_map[vj]].horiz) and
      (page_rules[verti_map[vi]].vert > page_rules[verti_map[vj]].vert)) then
        begin vt:=verti_map[vi]; verti_map[vi]:=verti_map[vj];
              verti_map[vj]:=vt end;

@ Traverse mapped rules looking for adjoining vertical rules. We actually
look for |height| overlap within |max_lap_rules|. Rules that have been
combined with other rules are set |covered := true|.
@<Traverse Verti...@>=
for vi:=1 to rules_on_page-1 do
 with page_rules[verti_map[vi]] do
  if not covered then
   for vj:= vi+1 to rules_on_page do
    if not page_rules[verti_map[vj]].covered then
     begin
     vt := vert - (page_rules[verti_map[vj]].vert -
                  page_rules[verti_map[vj]].height);
     if ((horiz = page_rules[verti_map[vj]].horiz) and
        (width = page_rules[verti_map[vj]].width) and
         (abs(vt) <= max_lap_rules)) then
       begin
        vert := page_rules[verti_map[vj]].vert;
        height := height + page_rules[verti_map[vj]].height - vt;
        page_rules[verti_map[vj]].covered := true;
       end;
     end;

@ Traverse mapped rules looking for adjoining horizontal rules. We actually
look for |width| overlap within |max_lap_rules|. Rules that have been
combined with other rules are set |covered := true|.
@<Traverse Horiz...@>=
for hi:=1 to rules_on_page-1 do
 with page_rules[horiz_map[hi]] do
  if not covered then
   for hj:= hi+1 to rules_on_page do
    if not page_rules[horiz_map[hj]].covered then
     begin
     ht := (horiz+width) - page_rules[horiz_map[hj]].horiz;
     if ((vert = page_rules[horiz_map[hj]].vert) and
        (height = page_rules[horiz_map[hj]].height) and
         (abs(ht) <= max_lap_rules)) then
       begin
        width := width + page_rules[horiz_map[hj]].width - ht;
        page_rules[horiz_map[hj]].covered := true;
       end;
     end;

@ @<Declare the function called |special_cases|@>=
function special_cases(@!o:eight_bits;@!p,@!a:integer):boolean;
label change_font,move_down,done,9998;
var q:integer; {parameter of the current command}
@!k:integer; {loop index}
@!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
@!pure:boolean; {is the command error-free?}
@!vvv:integer; {|v|, rounded to the nearest pixel}
begin pure:=true;
case o of
@t\4@>@<Cases for vertical motion@>@;
@t\4@>@<Cases for fonts@>@;
four_cases(xxx1): @<Translate an |xxx| command and |goto done|@>;
pre: begin error('preamble command within a page!'); goto 9998;
  end;
@.preamble command within a page@>
post,post_post: begin error('postamble command within a page!'); goto 9998;
@.postamble command within a page@>
  end;
othercases begin error('undefined command ',o:1,'!');
  goto done;
@.undefined command@>
  end
endcases;
move_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>;
change_font: @<Finish a command that changes the current font,
  then |goto done|@>;
9998: pure:=false;
done: special_cases:=pure;
end;

@ If we meet a |bop|, something's gone amiss, because |do_page| should never
see one of these commands.

When |eop| is met, we have to cater for two possibilities:
\item{$\bullet$} It's the end of a page in the \.{DVI} file, in which case
we must output all the glyphs, rules and bitmaps that we've accumulated;
having done this, we reset |max_h_so_far| and |max_v_so_far| so that each
page's dimensions are considered under the same conditions.
\item{$\bullet$} It terminates a stored sequence of |dvi| bytes
corresponding to a character taken from a virtual font.

If |push| is met, we don't bleat about exceeding the maximum stack size
reported by \TeX\ if the command has been read from a virtual character
sequence.

@<Cases for commands |nop|, |bop|, \dots, |pop|@>=
nop: goto done;
bop: begin error('bop occurred before eop!'); goto 9998;
@.bop occurred before eop@>
  end;
eop:if vf_take<0 then
    begin
      if s<>0 then error('stack not empty at end of page (level ',
            s:1,')!');
@:Error: stack not empty}{\quad\.{stack not empty...}@>
      if max_h<>max_h_saved then
        warning('Page wider than TeX reported by ',
                     (max_h-max_h_saved)/65536.0:1:4,'pt');
@:Warning: Page wider than TeX reported}{\quad\.{Page wider than }\TeX\.{...}@>
      if max_v<>max_v_saved then
        warning('Page deeper than TeX reported by ',
                     (max_v-max_v_saved)/65536.0:1:4,'pt');
@:Warning: Page deeper than TeX reported}{\quad\.{Page deeper than }\TeX\.{...}@>
      @<Move to top of form@>;@/
      @<Combine and...@>@;
      do_page:=true;@/
      @<Output contents of page@>;@/
      write(ln3_file,LF);               {LF after page}
      new_orient:=orientation;
      @<Change page orientation@>;      {reset, if necessary}
      max_h:=max_h_saved; max_h_so_far:=max_h;
      max_v:=max_v_saved; max_v_so_far:=max_v;
      top_of_page:=true;
      goto 9999;
    end else
    begin
      do_page:=true;
      goto 9999
    end;
push: begin
  if s=max_s_so_far then
    begin max_s_so_far:=s+1;
    if (s=max_s) and (vf_take<0) then
      error('push deeper than claimed in postamble!');
@:Error: push deeper than claimed}{\quad\.{push deeper than claimed...}@>
    if s=stack_size then
      begin capacity_exceeded('stack size=',stack_size:1);
@:capacity exceeded stack size}{\quad\.{stack size}@>
        goto 9998;
      end;
    end;
  hstack[s]:=h; vstack[s]:=v; wstack[s]:=w;
  xstack[s]:=x; ystack[s]:=y; zstack[s]:=z;
  hhstack[s]:=hh; vvstack[s]:=vv; incr(s); goto done;
  end;
pop: begin
  if s=0 then error('pop illegal at level zero')
@:Error: pop illegal at level zero}{\quad\.{pop illegal at level zero}@>
    else  begin decr(s);
    if (vv <> vvstack[s]) and (vvstack[s]>=0) then finish_seg;
    {different row of pixels}
    hh:=hhstack[s]; vv:=vvstack[s];
    h:=hstack[s]; v:=vstack[s]; w:=wstack[s];
    x:=xstack[s]; y:=ystack[s]; z:=zstack[s];
    end;
  goto done;
  end;

@ Rounding to the nearest pixel is best done in the manner shown here, so as
to be inoffensive to the eye: When the horizontal motion is small, like a
kern, |hh| changes by rounding the kern; but when the motion is large, |hh|
changes by rounding the true position |h| so that accumulated rounding errors
disappear. We allow a larger space in the negative direction than in
the positive one, because \TeX\ makes comparatively
large backspaces when it positions accents.

@d out_space==if (p>=font_space[cur_font])or(p<=-4*font_space[cur_font]) then
    hh:=pixel_round(h+p)
  else hh:=hh+pixel_round(p);
  q:=p; goto move_right

@<Cases for horizontal motion@>=
four_cases(right1):begin out_space @+end;
w0,four_cases(w1):begin w:=p; out_space @+end;
x0,four_cases(x1):begin x:=p; out_space @+end;

@ Vertical motion is done similarly, but with the threshold between
``small'' and ``large'' increased by a factor of five. The idea is to make
fractions like ``$1\over2$'' round consistently, but to absorb accumulated
rounding errors in the baseline-skip moves.

@d out_vmove==if abs(p)>=5*font_space[cur_font] then vv:=pixel_round(v+p)
  else vv:=vv+pixel_round(p);
  finish_seg;
  goto move_down

@<Cases for vertical motion@>=
four_cases(down1):begin out_vmove @+end;
y0,four_cases(y1):begin y:=p; out_vmove @+end;
z0,four_cases(z1):begin z:=p; out_vmove @+end;

@ @<Cases for fonts@>=
sixty_four_cases(fnt_num_0): goto change_font;
four_cases(fnt1): goto change_font;
four_cases(fnt_def1): begin define_font(p); goto done; end;

@ We need a certain amount of extra space in order to store the details of
\.{\BS special} commands as we work on them.

@<Glob...@>=
@!spec_par:packed array [1..max_special] of ASCII_code; {used to hold a
\.{\BS special} command}
@!spec_len: integer; {the space-stripped length of a \.{\BS special} command}
@!spec_ptr: integer; {pointer while decoding a \.{\BS special} command}
@!spec_cmd: str_number; {identifies a command string in a \.{\BS special}}
@!spec_unsupported : boolean;
@!spec_ch : ASCII_code;
@!point : array [1..max_points] of point_type;

@ This data structure records the co\"ordinates of a point generated by the
\.{defpoint} \.{\BS special}.  Such co\"ordinates are recorded in LN03
dimensions.

@<Types...@>=
@!point_type = packed record
 @!hh,@!vv : integer;
 @!present : boolean;
end;

@ Let's preset that which we've just defined\dots

@<Preset...@>=
for k:=1 to max_points do point[k].present:=false;

@ |xxx| commands in the DVI file arise from \.{\\special} commands in the
{\TeX} source file. \.{\\special} commands may be used for a variety of
things, so we must decode them further here.

We support two sorts of \.{\\special} name. The first derives from the
original (pre-V3) of this driver, of which there is but one example,
`\.{\\special\{SX\ file-name\}}'. The second sort takes the form:\par
\leavevmode\hbox{`\.{\\special\{LN03:}\meta{function-name}[\.\ %
\meta{other-parameters}[\.\ \dots]]\.\}'},\par
\noindent as in Flavio Rose's original. The \meta{function name}s
\.{landscape} and \.{portrait} can appear with or without the \.{LN03:}
prefix We support both versions, with the addition (over early versions of
this driver) that command decoding is case-insensitive.

This module does various sanity checks on the \.{\BS special} command's
parameter, ending up with its characters in |spec_par|, the count of characters
in |spec_len|, and |spec_ptr| pointing to the first character not yet `looked
at'.

The module (if successful) calls one of the subsidiary routines
|Copy_sixel_file|, |define_point|, |connect_points| or |reset_points|, actually
to implement the \.{\BS special} command before going to |done|.

@<Translate an |xxx| command and |goto done|@>=
begin bad_char:=false;
if p<0 then error('\special string of negative length!');
@:Error: special string of negative length}{\quad\.{\\special string of negative length}@>
if p>max_special then
begin error('\special string too long!');
@:Error: special string too long}{\quad\.{\\special string too long}@>
  p:=max_special
end;
for k:=1 to p do
  begin q:=get_byte;
  if (q<" ")or(q>"~") then bad_char:=true else
  spec_par[k]:=q;
  end;

{intialize the \.{\\special} decoding routines}
spec_len:=p; spec_ptr:=1;

{\dots\ and start decoding!!!}
@<Find start of \.{\\special}'s string(s)@>;
if not bad_char then begin
@<Match first command in \.{\\special}@>;
@<Match the \meta{function name} in a \.{\\special}@>;
end;
if bad_char then error('non-ASCII character in \special command!')
@:Error: non-ASCII character in special}{\quad\.{non-ASCII character in \\special}@>
else if spec_unsupported
then spec_error('Unsupported \special ');
@:Error: Unsupported special}{\quad\.{Unsupported \\special}@>
spec_unsupported:=false;
goto done;
end

@ If there's any space characters at the start of the \.{\\special}
string, we can safely skip past them\dots

@<Find start of \.{\\special}'s string(s)@>=
if rd_nb_char(spec_ch) then
  begin
  error('Null \special argument!');
@:Error: Null special argument}{\quad\.{Null \\special argument}@>
  goto done;
  end;
bksp_char

@ We have a very limited choice of commands that may be given without being
preceded by the device specifier `\.{ln03:}'\dots

@<Match first command in \.{\\special}@>=
spec_cmd := find_match;
case spec_cmd of
  spec_sx: spec_cmd := spec_six;        {Substitute |'ln03:sixel'| for |'sx'|}
  spec_ln: spec_cmd := find_match;      {Find string following |'ln03:'|}
  spec_land, spec_port: do_nothing;     {Recognize again in next section}
  othercases
    spec_cmd:=0;    {Command \&{must} start with |'sx '| or |'ln03:'|!}
endcases

@ The majority of \.{\\special} \meta{function name}s will be recognized
here\dots

@<Match the \meta{function name} in a \.{\\special}@>=
case spec_cmd of
  spec_land: new_orient:=landscape; {|'landscape'| or |'ln03:landscape'|}
  spec_port: new_orient:=portrait;  {|'portrait'| or |'ln03:portrait'|}
  spec_six, spec_plt:           {|'ln03:sixel'| or |'ln03:plotfile'|}
        include_sixel_dump;
  spec_set:                     {|'ln03:defpoint'|}
        define_point;
  spec_con:                     {|'ln03:connect'|}
        connect_points;
  spec_rst:                     {|'ln03:resetpoints'|}
        reset_points;
  othercases
    spec_unsupported:=true
endcases


@ @<Translate a |set_char|...@>=
goto fin_set

@ @<Finish a command that either sets or puts a character...@>=
if p<0 then p:=255-((-1-p) mod 256)
else if p>=256 then p:=p mod 256; {width computation for oriental fonts}
@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
if (p<font_bc[cur_font])or(p>font_ec[cur_font]) then q:=invalid_width
else q:=cur_font_glyph(p).width;
if q=invalid_width then
  begin error('character ',p:1,' invalid in font ');
@.character $c$ invalid...@>
  print_font(cur_font);
  if cur_font<>nf then print('!'); {font |nf| has `\.!' in its name}
  end;
  if font_type[cur_font]=missing then
    @<Substitute a solid rule for character from missing font@>
  else
    ord_text(p);
if o>=put1 then goto done;
if q=invalid_width then q:=0
else hh:=hh+cur_font_glyph(p).pixel_width;
goto move_right

@ @<Finish a command that either sets or puts a rule...@>=
q:=signed_quad;
if (p>0)and(q>0) then @<Collect Rules...@>@;
if o=put_rule then goto done;
hh:=hh+rule_pixels(q); goto move_right

@ A sequence of consecutive rules, or consecutive characters in a fixed-width
font whose width is not an integer number of pixels, can cause |hh| to drift
far away from a correctly rounded value. \.{DVItoLN03} ensures that the
amount of drift will never exceed |max_drift| pixels.

Since \.{DVItype} is intended to diagnose strange errors, it checks
carefully to make sure that |h| and |v| do not get out of range.
Normal \.{DVI}-reading programs need not do this, but \.{DVItoLN03} makes some
of these same checks and reports characters which are ``set'' outside the page
(as reported by \TeX\ itself).

No warning is given when |max_h_so_far| exceeds |max_h| by less than~100,
since 100 units is invisibly small; it's approximately the wavelength of
visible light, in the case of \TeX\ output. Rounding errors can be expected
to make |h| and |v| slightly more than |max_h| and |max_v|, every once in
a~while; hence small discrepancies are not cause for alarm.

@d infinity==@'17777777777 {$\infty$ (approximately)}
@d max_drift=2 {we insist that abs|(hh-pixel_round(h))<=max_drift|}

@<Finish a command that sets |h:=h+q|, then |goto done|@>=
if (h>0)and(q>0) then if h>infinity-q then
  begin error('arithmetic overflow! parameter changed from ',@|
@.arithmetic overflow...@>
    q:1,' to ',infinity-h:1);
  q:=infinity-h;
  end;
if (h<0)and(q<0) then if -h>q+infinity then
  begin error('arithmetic overflow! parameter changed from ',@|
    q:1,@| ' to ',@|(-h)-infinity:1);
  q:=(-h)-infinity;
  end;
@<Record current position of any text output@>;
hhh:=pixel_round(h+q);
if abs(hhh-hh)>max_drift then
  if hhh>hh then hh:=hhh-max_drift
  else hh:=hhh+max_drift;
h:=h+q;
if abs(h)>max_h_so_far then
  begin if abs(h)>max_h+h_fuzz then
    max_h:=abs(h);
  max_h_so_far:=abs(h);
  end;
goto done

@ If the |cur_font| wasn't available for downloading to the LN03, instead of
imaging the glyph we output a solid rule of the dimensions given by the
\.{TFM} file (on the proviso that the \.{DVI} file couldn't have been
generated by \TeX\ if the font was totally unknown).

@<Substitute a solid rule for character from missing font@>=
  with cur_font_glyph(p) do
    Collect_rule(hh,vv+pixel_depth,pixel_width,pixel_height+pixel_depth)

@ Procedure |do_page| gets to label |move_right| to record the rightmost
excursion of any character that's just been set: however, with virtual
fonts, we can end up with |cur_seg=nil|.

@<Record current position of any text output@>=
if ((o<set_char_0+128) or
  ((o>=set1) and (o<set1+4)) or
  ((o>=put1) and (o<put1+4))) and
  (cur_seg<>nil) then
    cur_seg^.xe:=x_pos;

@ @<Finish a command that sets |v:=v+p|, then |goto done|@>=
if (v>0)and(p>0) then if v>infinity-p then
  begin error('arithmetic overflow! parameter changed from ',
@.arithmetic overflow...@>
    p:1,' to ',infinity-v:1);
  p:=infinity-v;
  end;
if (v<0)and(p<0) then if -v>p+infinity then
  begin error('arithmetic overflow! parameter changed from ',
    p:1, ' to ',(-v)-infinity:1);
  p:=(-v)-infinity;
  end;
vvv:=pixel_round(v+p);
if abs(vvv-vv)>max_drift then
  if vvv>vv then vv:=vvv-max_drift
  else vv:=vvv+max_drift;
v:=v+p;
if abs(v)>max_v_so_far then
  begin if abs(v)>max_v+v_fuzz then
    max_v:=abs(v);
  max_v_so_far:=abs(v);
  end;
goto done

@ @<Finish a command that changes the current font...@>=
font_num[nf]:=p; cur_font:=0;
while font_num[cur_font]<>p do incr(cur_font);
if cur_font=nf then
  error('reference to an unloaded font');
@:Error: reference to unloaded font}{\quad\.{reference to unloaded font}@>
cur_font:=font_map[cur_font]; cur_base:=glyph_base[cur_font];
finish_seg;
goto done

@* Font usage pass.  Whilst gathering the font usage statistics, each |bop|
command will cause various variables to be updated which the ``real'' pass
through the file would not expect to have changed.  Therefore, we copy the
state of these variables after recognising the start of the first page
requested, and restore them (and the file position), before commencing the
true output pass.  The Boolean |scanning| inhibits output to the \.{TYP} file
during this phase.

@<Globals...@>=
@!start_page_count:integer;
@!scanning:boolean;

@ @<Preset...@>=
scanning:=false;

@ Having found the first page which is eligible for translation to LN03
format, we skip rapidly through the remainder of the file, noting whether any
particular character of each font is actually used in the output.  The file is
then further read until the postamble is reached, and the character usage
information used to generate the appropriate down-line loadable font(s) for
the LN03.
@^down-line loading of fonts@>

@<Gather font usage statistics@>=
begin
  monitor('Gathering font usage statistics...');
@.Gathering font usage statistics@>
  start_loc:=old_backpointer; { Remember where the first page started }
  start_page_count:=page_count-1; { Preserve, since |bop|s will change this }
  scanning:=true;@/
  skip_pages; { Note usage of any required characters }
  scanning:=false; {We've completed the scanning phase}
  if signed_quad<>old_backpointer then
    error('backpointer in byte ',cur_loc-4:1,
        ' should be ',old_backpointer:1,'!');
@:Error: backpointer in byte should be}{\quad\.{backpointer...should be...}@>
  substitution_pass:=true;      {Prevents spurious ``font already defined''s}
  read_postamble;@/
  @<Assign font/char mapping@>;@/
  @<Download font files@>;@/
  @<Restore pointers after font usage pass@>;@/
  in_postamble:=false;
end

@ After we've finished the font usage pass, we must restore all the various
pointers into the |dvi_file| to their state preceding the pass, so that the
main program can just carry on with the actual setting of the glyphs.

@<Restore pointers after font usage pass@>=
old_backpointer:=start_loc;     {for when the next |bop| is met}
move_to_byte(start_loc+1);      {Go back to just after |bop| of starting page}
for k:=0 to 9 do count[k]:=signed_quad; {fill in page number array}
p:=signed_quad; {Swallow the |old_backpointer|}
page_count:=start_page_count

@*Map characters to LN03 fonts. At this point, we know which characters have
actually been invoked in each of the \TeX\ fonts.  Since the LN03 has
limited font memory, as many characters as practicable are packed into a
188-character LN03 font; these characters will typically be taken from at
least two \TeX\ fonts.
@^LN03 laser printer@>

We therefore require to know, for each \TeX\ character, to which LN03 font it has
been allocated, and what character value it occupies in that font.  When
characters are actually being typeset, only ten LN03 fonts are directly
accesible at any one time (corresponding to the Select Graphic Rendition
parameters 10..19); if the character to be typeset is allocated to a different
LN03 font, a suitable escape sequence will need to be output to change the
font which the LN03 is accessing.

The following declarations accomplish this mapping$\ldots$

@d left_first="!"       {These are the ``printable'' character ranges}
@d left_last="~"        {which may appear in an LN03 font}
@d right_first=left_first+128
@d right_last=left_last+128

@ For any particular \TeX\ font, |the_txf|, we count in
|font_occupancy[the_txf]| the number of characters invoked from that font.
To save all that indexing of arrays for this, the counting actually takes
place in |usage|.  The array |txf_to_lnf| provides the number of the LN03
internal font into which |the_txf| has been mapped, whilst |txf_ord| is a
list of |the_txf|s in the order in which they were mapped (largest first).
For each of the LN03's internal fonts, |lnf|, the array element
|last_lnf[lnf]| contains the character code of the last character used
therein to map a character. |ch_widths| is an array which records the actual
width, in pixels, of each character created within an LN03 font.

The scalar variables used in this section include: (1) |txf_size| which
records how many characters are used in the next largest \TeX\ font
remaining; (2)  |lnf_spare| records the number of unallocated glyphs
remaining in the LN03 font-file currently being filled; (3) |total_rasters|
counts the number of bytes of character rasters downloaded (this is reported
on the terminal and the \.{TYP} log file, and may be important to
installations without additional RAM font memory on the LN03); (4)
|pxl_ident| reminds us whether |the_txf| exists as a packed or unpacked
pixel file.

@<Globals in...@>=
@!font_occupancy:packed array [0..max_fonts] of 0..256; {Number of characters used}
@!usage:-1..256;
@!next_lnf:0..max_lnfonts+1;    {Next LN03 font index to be allocated}
@!next_txf:0..max_fonts;        {Next \TeX\ font to be mapped}
@!txf_size:-1..256;             {Occupancy of largest remaining \TeX\ font}
@!txf_to_lnf:packed array [0..max_fonts] of -1..max_lnfonts; {Maps \TeX\ font to LN03 font}
@!txf_ord:packed array [0..max_fonts] of -1..max_fonts; {Contains \TeX\ font numbers in
order of mapping}
@!the_txf:0..max_fonts; {TeX font currently being mapped}
@!lnf_spare:integer;    {``Spare'' room in current LN03 font}
@!last_lnf:packed array [0..max_lnfonts] of left_first..right_last;
{``Last'' character allocated in an LN03 font}
@!total_rasters:integer;        {Total size of down-loaded fonts}
@!ch_widths:array [0..max_lnfonts,left_first..right_last] of integer;
@!pxl_ident:pixel_types;

@ At present, none of the LN03 fonts are `in use'; neither have any of them
been mapped to |SGR| (Select Graphic Rendition) codes.

We also clear out the variable in which we accumulate the count of all bytes
transferred as character rasters.

@<Set initial...@>=
for m:=0 to max_lnfonts do font_in_use[m]:=-1;
for m:=0 to max_SGR do who_uses[m]:=-1;
total_rasters:=0;

@ We now perform the actual mapping.  Each \TeX\ font is considered in turn,
starting with the largest (most densely occupied) as yet unallocated.  The
characters of this font are mapped to the next available LN03 font, and the
remaining space in the LN03 font is allocated to the largest \TeX\ fonts which
will fit.  When no such \TeX\ font will fit in the remaining space, a new LN03
font is ``opened''.

Before we go ahead and map \TeX\ characters to physical ones, we have to
ensure that we're not still considering virtual fonts.

@<Assign font/char mapping@>=
@<Remap any virtual fonts@>;
@<Determine occupancy of each \TeX\ font@>;
next_lnf:=0; next_txf:=0;       {Order of fonts}
repeat
  lnf_spare:=left_last-left_first+1+right_last-right_first+1; {therefore =
  188}
  txf_size:=-1;         {We're searching for largest font}
  @<Find largest unallocated font@> ;
  if txf_size>0 then    {Assuming there are any unallocated}
  begin
    if next_lnf>max_lnfonts then capacity_exceeded('too many LN03 fonts');
@:capacity exceeded too many LN03 fonts}{\quad\.{too many LN03 fonts}@>
    m:=left_first-1;            {We'll start with the ``!'' character}
    @<Map characters in \TeX\ font to allocated LN03 font@>;
    @<Fit further \TeX\ fonts into LN03 font file@>;
    incr(next_lnf)
  end;  {Fitted largest \TeX font, and some more, into an LN03 font}
until txf_size<=0;      {Repeat until all fonts mapped}

@ We firstly require to know how many different characters have been invoked
from each of the \TeX\ fonts used in the document.  The following section of
code performs this feat.


To speed things up, we avoid the double indexing of |glyphs| that would
occur if we used |glyph_map|; these definitions help.

@d first_glyph_index(#)==glyph_base[#]+font_bc[#]
@d last_glyph_index(#)==glyph_base[#]+font_ec[#]
@<Determine occupancy of each \TeX\ font@>=
for m:=0 to nf-1 do     {Determine how occupied each \TeX\ font is}
begin usage:=0;
  for n:=first_glyph_index(m) to last_glyph_index(m) do
    if glyphs[n].loaded=wanted then incr(usage);
  font_occupancy[m]:=usage;
  txf_ord[m]:=-1;       {Font unallocated}
  txf_to_lnf[m]:=-1
end

@ Because we want to avoid having a single \TeX\ font overlapping two separate
fonts in the LN03 (which would require more control sequences to be output),
we prefer to have a large font starting off a new |ln_font|.  This can be
achieved by finding the largest font that has not yet been allocated a
``home''.

(After mapping this font, we can use up the remaining unallocated characters of
the |ln_font| by putting smaller \TeX\ fonts into the same |ln_font|.)

It might prove embarrassing to attempt to map a virtual font, so we only
load fonts that are marked as |wanted|; this excludes |virtual| fonts, and
also those referenced from such a font from which all the glyphs are
|unused|.

@<Find largest unallocated font@>=
for n:=0 to nf-1 do
if (font_type[n]=wanted) and (font_occupancy[n]>txf_size) then
  begin txf_size:=font_occupancy[n];
    the_txf:=n
  end
@ Having fitted a ``fullish'' \TeX\ font into a fresh LN03 font, we now
attempt to fill up the space remaining in the latter by squeezing in as many
smaller fonts as will fit.  |lnf_spare| has been set to indicate how many
unallocated glyphs remain in the LN03 font, so we find the largest font whose
occupancy is not greater than this amount, and map it in; this process is
repeated until there is no \TeX\ font remaining that will fit in the
|lnf_spare| space remaining, at which point a new LN03 font is started (if
necessary).

@<Fit further \TeX\ fonts into LN03 font file@>=
while lnf_spare>0 do    {Now find any \TeX\ font that fits in remainder}
begin  usage:=-1;       {Performs same function as |txf_size| above}
  for n:=0 to nf-1 do   {Find largest remaining that fits}
    if (font_type[n]=wanted) and {provided it's physical, unmapped and}
      (font_occupancy[n]>usage) and {it's more densely used}
      (font_occupancy[n]<=lnf_spare) then {and it fits in space available}
    begin usage:=font_occupancy[n];
      the_txf:=n
    end;@t\2@>
  if usage > 0 then             {There's one that fits!}
  begin
    m:=last_lnf[next_lnf];      {Last character allocated in LN03 font}
    @<Map characters in \TeX\...@>;
  end  else
    lnf_spare:=0;       {Nothing else fits; force new LN03 font}
end  { while spare space remains in LN03 font}


@ The following code maps the characters in the current (|the_txf|) \TeX\ font
to character positions in the current (|next_lnf|) LN03 font.  No attempt is
made to make the mapped characters intelligible if the \.{LN3} file is
printed; this would be a ``nice'' revision of the program, but well nigh
impossible!

@<Map characters in \TeX\...@>=
txf_to_lnf[the_txf]:=next_lnf;  {Map \TeX\ font to LN03 font}
txf_ord[next_txf]:=the_txf;     {Save \TeX\ font numbers, largest first}
font_type[the_txf]:=yes;        {Avoid considering this font again!}
incr(next_txf);
for k:=first_glyph_index(the_txf) to last_glyph_index(the_txf) do
  with glyphs[k] do
  if loaded=wanted then {Work through the characters of the \TeX\ font}
  begin
    @<Put LN03 code for this \TeX\ character into |m|@> ;
    decr(lnf_spare);    {Count one less free}
    char_code:=m;
  end;
last_lnf[next_lnf]:=m   {Note ``last'' character allocated in LN03 font}


@ As we allocate characters from |the_txf| to the LN03 font |next_lnf|, we
have to ensure that the character codes used are those of \&{printing}
characters on the LN03.  The LN03 uses the customary \.{C0} and \.{C1} control
code sets of the ISO/ASCII standards, and so the only printable characters are
`\.\SP' to `\.\TL' (|left_first..left_last|) and the corresponding
positions with the eighth bit set (|right_first..right_last|).  This coding
scheme has ``holes'' in it, so we need to make appropriate adjustments.

Furthermore, if we permit a large font (with more than 188 characters used) to
span across into a second LN03 internal font, we have to make the transition
to the beginning of the second font, noting the false ``last character used''
and revising |lnf_spare| to reflect the new (unused) font.

@<Put LN03 code for this \TeX\ character into |m|@>=
if m=right_last then  {Start another LN03 font for spanning \TeX\ font}
  begin last_lnf[next_lnf]:=right_last;
    lnf_spare:=left_last-left_first+1+right_last-right_first+1;
    m:=left_first;
    incr(next_lnf)
  end
else
  if m=left_last then m:=right_first {make transition to GR set in font}
  else incr(m); {Next free character in LN03 font}

@*Generating LN03 font for downloading.
The following code generates the content of an LN03 font file, in memory,
and then calls |add_txf_to_lnf| to put the rasters into this file.
The file type extension is \.{.LN3}, and this file is always opened in the
user's default directory.


The next section of code actually generates the \.{LN3} file, starting with
its ``set up'' information.  The program continues by creating and downloading
each necessary LN03 font, by successive calls of the function
|add_txf_to_lnf|, which returns as result the number of bytes downloaded in
the rasters of the characters contained therein; this is added to
|total_rasters| so that DVItoLN03 can report the size of all the fonts loaded.

The first (or only) LN03 font file downloaded is introduced by the |DCS|
(Device Control Sequence) |'0;1;0y'|; these three parameters indicate,
respectively, that the font file is in Digital font file format, is \&{not} to
print a summary sheet, and is to replace all previously downloaded fonts.
Successive LN03 fonts are separated from the previous ones by a simple |','|
character; whilst the end of the font download is marked by the |ST| (String
Terminator) character --- this is preceded by a textual comment.

@<Download font files@>=
@<Generate \.{LN3} file header@>;
total_rasters:=0;
if next_txf > 0 then            {Unlikely that no characters were used, but...}
begin
  write_ln(ln3_file,dcs,'0;1;0y');      {Replace all fonts with these}
  for m:=0 to next_txf-1 do     {Cycle through the ``used'' \TeX\ fonts}
  begin k:=txf_ord[m];  {Get \TeX\ font number corresponding to next largest font}
    name_pxl_file(k,file_name,pxl_ident);
    if (m>0) then
      @<Separate LN03 internal fonts@> ;
    @<Copy glyphs to LN03 font; report size of rasters@>
  end;
  write_ln(ln3_file,';DVItoLN03 font load',st); {End of all fonts}
end;
print_ln('Total of ',total_rasters:1,' bytes of rasters downloaded');
write_ln(term_out,'Total of ',total_rasters:1,
                        ' bytes of rasters downloaded',crlf);
@:Total of n bytes of rasters}{\.{Total of $n$ bytes of rasters...}@>
@<Complete mapping of virtual characters@>; {point to the real ones}
{Set page limits}
write(ln3_file,csi,x_min:1,';',page_wid:1,'s',csi,y_min:1,';',page_len:1,'r');
@<Designate first 10 fonts to SGRs |10..19|@>@;

@ We start by writing the LN03 directives to the front of the file.  Since the
introduction of the LN03-Plus, this has been made more complicated, because
the sequence |esc, 'c'| is meaningful (and \&{not} as |RIS|!) when the
LN03-Plus is in the Tektronix emulation mode.  Therefore, we start off by
outputting the sequence |esc, '[!p'| which is DEC's private ``soft terminal
reset'' (|DSCSTR|).  (Note that this sequence uses the ``long-winded'' way of
specifying |csi|; this is because the LN03-plus ignores the eigth bit when it
is emulating a Tektronix terminal.)  However, this won't necessarily reset an
`ordinary' LN03, so we output the ISO-standard |RIS| (Reset Initial State
$\equiv$ |esc,'c'|) sequence as well.

There then follow a number of control sequences which set up further
characteristics of the LN03: (1) |'?27h'| is a DEC private control sequence,
|DECPSP|, which selects proportional spacing mode, whereby the printer moves
forward by the recorded width of the character glyph rather than by some fixed
amount after printing; (2) |'11h'| \&{set}s the |PUM| (Positioning Unit Mode),
when set, this mode indicates that ``cursor movement'' sequences work in units
of either decipoints or pixels, which of these is determined by (3) |'7 I'|
which sets the |SSU| (Select Size Unit) to be in units of a single pixel; (4)
|'?52h'| is another DEC private sequence, \&{set}ting the |DECOPM| (Origin
Placement Mode) which places the origin for all measurement to the upper-left
corner of the physical page (the alternative is 0.25 inches from that point).
Finally, the sequence ending in |'t'| contains the |page_len| as a parameter,
and is another DEC private control sequence which sets the |DECLPP| (Lines per
Physical Page) to be the height of the printable area, in pixels.

@d DECSTR==esc+'[!p'
@d RIS==esc+'c'
@d DECPSP_on==csi+'?27h'
@d PUM_on==csi+'11h'
@d SSU_pixel==csi+'7 I'
@d DECOPM_on==csi+'?52h'

@<Generate \.{LN3} file header@>=
if orientation=landscape then
  write_ln(ln3_file,DECSTR,RIS,csi,PFS_landscape)
else
  write_ln(ln3_file,DECSTR,RIS,csi,PFS_portrait);
cur_orient:=orientation; new_orient:=orientation;
write_ln(ln3_file,DECPSP_on,PUM_on,SSU_pixel,DECOPM_on,csi,page_len:1,'t');
@<Select printing mode and feed tray on DEClaser printers@>

@ If we are starting the download of any except the first \TeX\ font, it may
be that the previous \TeX\ font was the last loaded into the current LN03
font, which means we will be starting a new LN03 font here.  Therefore we need
to separate the two LN03 fonts from each other: the comma (`\.,') character is
used by the LN03 for this purpose.

@<Separate LN03 internal fonts@>=
if (txf_to_lnf[k]<>txf_to_lnf[txf_ord[m-1]]) then
      write_ln(ln3_file,',')

@ The function |add_txf_to_lnf| performs all the work of creating an LN03 font
from the rasters in the font file.  The result returned by this function
gives the total number of bytes created in the LN03 font for the given \TeX\
file.

@<Copy glyphs to LN03 font; report size of rasters@>=
total_rasters:=total_rasters+
  add_txf_to_lnf(txf_to_lnf[k],k,file_name,pxl_ident)

@ We may have both packed and unpacked files available from which to take
pixel bitmaps, so we require some indication of what type of file is
actually available.

@<Types...@>=
@!pixel_types = (@!unavailable,@!packed_file,@!pixel_file);

@ The user has the option of using either pixel files (\.{.PXL}) and/or packed
pixel files (\.{.PK}) as the source of font raster information.  There is the
further option of the directories in which these files are held being either
``flat'' (with all font files of one class in the same directory) or
``nested'' (where separate sub-directories are provided for each different
magnification).

File naming conventions used are illustrated by the following examples, which
show the names under which \.{cmr10} will be found at magsteps 0--2:

\yskip\centerline{\vbox{\offinterlineskip
  \hrule
  \halign{&\vrule#&\strut\hfil#\hfil&\quad\tt#\quad&\quad\tt#\quad&
      \quad\tt#\quad&\quad\tt#\quad&\vrule#\cr
    height2pt&\multispan5 &height2pt\cr
    &\omit\strut Magnif-\hfil&\multispan2\hfil Packed Pixels\hfil&
      \multispan2\hfil Unpacked Pixels\hfil&\cr
    &\omit\strut\hfil ication&\hfil\rm Flat\hfil&\hfil\rm Nested\hfil&
        \hfil\rm Flat\hfil&\hfil\rm Nested\hfil&\cr
    height2pt&\multispan5 &height2pt\cr
    \noalign{\hrule}
    height2pt&\multispan5 &height2pt\cr
    &0&CMR10.300PK&[300]CMR10.PK&CMR10.1500PXL&[1500]CMR10.PXL&\cr
    &$1\over2$&CMR10.329PK&[329]CMR10.PK&CMR10.1643PXL&[1643]CMR10.PXL&\cr
    &1&CMR10.360PK&[360]CMR10.PK&CMR10.1800PXL&[1800]CMR10.PXL&\cr
    &2&CMR10.432PK&[432]CMR10.PK&CMR10.2160PXL&[2160]CMR10.PXL&\cr
    height2pt&\multispan5 &height2pt\cr}
  \hrule
}}

The program determines which class(es) of files are available from the
qualifiers \.{/PXL\_FONT\_DIRECTORY} (for the unpacked files) and
\.{/PK\_FONT\_DIRECTORY} (for the packed files).  At least one of these
qualifiers must be present; if the qualifier provided is a VMS logical name,
it has been translated repeatedly until a VMS directory specification resulted.
Examination of (the first translation of) this specification then determines
whether the flat or nested structure is being used: if the directory
specification ends with the characters `\.{.]}' it indicates that the raster
files will be found in sub-directories, whilst absence of the `\..' indicates
a flat structure.  The variables |pk_rooted| and |pxl_rooted| will already
have been set to reflect this.

Whether packed or unpacked pixel files are used, it is necessary to
compute a number which relates the magnification to either the sub-directory
name or the file extension.  Since actual file names may well have been
computed using low-accuracy (possibly integer) arithmetic by operating system
commands, the possibility exists that the numbers may have been truncated to
the ``wrong'' integer compared with those that \.{DVItoLN03} computes;
therefore, we always arrange to look for the files using both the computed
number and those $\pm1$ from it.

The variable |dir_off| is used to compute the offset from the central figure,
which is held in |p|.  The variables |file_stat| and |find_ctxt| are used in
conjunction with Vax/VMS system calls to determine whether the requested file
exists; |found_file| takes the values $-1$, indicating that the file wasn't
found, or $+1$ if it is found.  It also can take the value $0$ whilst the
search is proceding, so a simple boolean cannot be used instead.

The following procedure converts the \TeX\ font referenced by |TeX_font| into
the correct strings for the directory and file name under which \.{DVItoLN03}
expects to find the pixel file.

@p procedure name_pxl_file ( @!TeX_font :  integer;@/
                             var @!open_file_name: file_spec;
                             var @!what_it_was: pixel_types);
   var @!p,@!dir_off,@!file_stat,@!find_ctxt,@!found_file : integer;@/
       @!found_name,@!expected_file_spec : file_spec;
begin
  found_file := -1;
  expected_file_spec:='';@/
  @<Locate packed pixel file@>;@/
  if found_file<0 then
    @<Locate unpacked pixel file@>;
  if found_file<0 then
  begin
    warning('cannot find file ',expected_file_spec);
@:Warning: cannot find file}{\quad\.{cannot find file}@>
    what_it_was:=unavailable
  end
end;


@ We only look for packed pixel files if the \.{/PK\_FONT\_DIRECTORY}
qualifier was present, in which case its value will have been placed into the
string |tex_pk_font|. If the final characters of (the first logical name
translation of) this string were `\.{.]}', then a nested directory structure
is assumed (as indicated in |pk_rooted|), and the file is sought in the form
\hbox{\meta{disk}\meta{directory}\.[\meta{size}\.]\meta{name}\.{.PK}}.
Alternatively, if the directory is flat, the filename sought will be
\hbox{\meta{disk}\meta{directory}\meta{name}\..\meta{size}\.{PK}}.

Unfortunately, it is necessary to reconstruct the desired string for each
offset tried.  The \meta{size} is effectively computed from 1.5
times the font design size compared with the \TeX ware default of 200
pixels/inch, yielding 300 for unmagnified fonts.

@d append_name==for n:=font_name[TeX_font] to font_name[TeX_font+1]-1 do
                  open_file_name:=open_file_name+xchr[names[n]]

@d flat_file_spec(#)==begin open_file_name := # ;
                      append_name;
                      open_file_name:=open_file_name+'.'+str_int(p+dir_off)
                      end

@d rooted_file_spec(#)==begin open_file_name:= # + '[' +
                                   str_int(p+dir_off) + ']';
                        append_name;
                        open_file_name:=open_file_name+'.'
                        end

@<Locate packed pixel file@>==
if tex_pk_font.length > 0 then
begin
  dir_off:=0;
  p:=round(((resolution/1000)*mag*font_scaled_size[TeX_font])/font_design_size[TeX_font]);
  repeat
    if not pk_rooted then
      flat_file_spec(tex_pk_font)
    else
      rooted_file_spec(tex_pk_font);
    open_file_name:=open_file_name+'PK';
    @<Attempt to find file with specification |open_file_name|@>
  until found_file<>0;
  if found_file>0 then
  begin
      open_file_name:=found_name;
      what_it_was:=packed_file
  end
end;

@ To determine the correct file of pixels to be read, the quantity |p| is
computed; since it is a \TeX ware convention that all pixel files are assumed
(at magnification=1000) to be at a |resolution| of 200 dots/inch, and the
LN03's |resolution| is \&{300} dots/inch, then an ordinary unmagnified font
will be sought with a
$\langle$\\{size}$\rangle$ of \.{1500}, whilst one magnified \.{\BS magstep 1}
will have a $\langle$\\{size}$\rangle$ of \.{1800}, \\{etc.}   Computation of
|p| yields the appropriate value for the Vax/VMS directory or file type to be
used, and |VAX_lib_find_file| is used to verify that the file exists; to allow
for rounding errors, the directories |p|$\pm1$ are also sought.

@<Locate unpacked pixel file@>==
if tex_pxl_font.length > 0 then
begin
  dir_off:=0;
  p:=round(((resolution/200)*mag*font_scaled_size[TeX_font])/font_design_size[TeX_font]);
  repeat
    if not pxl_rooted then
      flat_file_spec(tex_pxl_font)
    else
      rooted_file_spec(tex_pxl_font);
    open_file_name:=open_file_name+'PXL';
    @<Attempt to find file with specification |open_file_name|@>
  until found_file<>0;
  if found_file>0 then
  begin
    open_file_name:=found_name;
    what_it_was:=pixel_file
  end
end

@ Now that we've got a full file specification (barring the version number) in
|open_file_name|, we call upon the Vax/VMS system services to try to locate
the file.  Before doing this, we make a note of the file specification if it's
that for the default file name (with |dir_off = 0|), and we haven't
previously noted a file name.

The find\_file system service requires a context variable (because it is
capable of finding a number of files matching a `wild-carded' file
specification), but we're not interested in that; therefore we always set
|find_ctxt| to zero before searching.  \&{N.B.} This may not be written as the
constant |0| in the procedure call, because the system service writes a value
back to this variable.

The system service returns an odd status if the search is successful;
otherwise we change |dir_off| to a suitable new value, unless we've already
tried |p| and $p\pm1$, in which case we indicate the absence of the file by
setting |found_file = -1|.

@<Attempt to find file with specification |open_file_name|@>=
if (dir_off=0) and (expected_file_spec='') then
  expected_file_spec:=open_file_name;
find_ctxt:=0;
file_stat:=VAX_lib_find_file(open_file_name,found_name,find_ctxt);
found_file:=0;
if odd(file_stat) then found_file:=1
else
begin
  if dir_off=1 then found_file:=-1;
  if dir_off=-1 then dir_off:=1 else dir_off:=-1
end

@ Although up to |max_lnfonts| may be downloaded by this program, the LN03 is
only capable of accessing any ten of these at any one time; it accomplishes
this by allocating the |SGR| (Select Graphic Rendition) designators |10..19|
to the first ten fonts downloaded.  Fonts are designated by means of their
16-character internal font identifiers, allocated during the generation of
each font.

@<Designate first 10 fonts to SGRs |10..19|@>=
for m:=0 to VAX_min(max_SGR,next_lnf-1) do      {Match first 10 fonts to |SGR| designators}
begin font_in_use[m]:=m; who_uses[m]:=m;
  write_ln(ln3_file,dcs,'1;1',m:1,'}U00000',m:1,'002SK00GG',st)
end;
write_ln(ln3_file,csi,'10m');   {|SGR| 10 --- select most used font}
cur_out:=0              {We won't need to |SGR| if first output uses |SGR| 10}

@*Format of Virtual Fonts.  The following description is cribbed almost
verbatim from Knuth's description of \.{VFtoVP}.
@^Knuth, D.~E.@>
@:VFtoVP program}{\.{VFtoVP} program@>

The idea behind \.{VF} files is that a general
interface mechanism is needed to switch between the myriad font
layouts provided by different suppliers of typesetting equipment.
Without such a mechanism, people must go to great lengths writing
inscrutable macros whenever they want to use typesetting conventions
based on one font layout in connection with actual fonts that have
another layout. This puts an extra burden on the typesetting system,
interfering with the other things it needs to do (like kerning,
hyphenation, and ligature formation).

These difficulties go away when we have a ``virtual font,''
i.e., a font that exists in a logical sense but not a physical sense.
A typesetting system like \TeX\ can do its job without knowing where the
actual characters come from; a device driver can then do its job by
letting a \.{VF} file tell what actual characters correspond to the
characters \TeX\ imagined were present. The actual characters
can be shifted and/or magnified and/or combined with other characters
from many different fonts. A virtual font can even make use of characters
from virtual fonts, including itself.

Virtual fonts also allow convenient character substitutions for proofreading
purposes, when fonts designed for one output device are unavailable on another.

@ A \.{VF} file is organized as a stream of 8-bit bytes, using conventions
borrowed from \.{DVI} and \.{PK} files. Thus, a device driver that knows
about \.{DVI} and \.{PK} format will already contain most of the mechanisms
necessary to process \.{VF} files.


A preamble appears at the beginning, followed by a sequence of character
definitions, followed by a postamble. More precisely, the first byte of
every \.{VF} file must be the first byte of the following ``preamble
command'':

\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]| |cs[4]| |ds[4]|.
Here |i| is the identification byte of \.{VF}, currently 202. The string
|x| is merely a comment, usually indicating the source of the \.{VF} file.
Parameters |cs| and |ds| are respectively the check sum and the design size
of the virtual font; they should match the first two words in the header of
the \.{TFM} file for the virtual font.

\yskip
After the |pre| command, the preamble continues with font definitions;
every font needed to specify ``actual'' characters in later
\\{set\_char} commands is defined here. The font definitions are
exactly the same in \.{VF} files as they are in \.{DVI} files, except
that the scaled size |s| is relative and the design size |d| is absolute:

\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |0<=k<256|.

\yskip\hang|@!fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |0<=k<65536|.

\yskip\hang|@!fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |0<=k<@t$2^{24}$@>|.

\yskip\hang|@!fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.

\yskip\noindent
These font numbers |k| are ``local''; they have no relation to font numbers
defined in the \.{DVI} file that uses this virtual font. The dimension~|s|,
which represents the scaled size of the local font being defined,
is a |fix_word| relative to the design size of the virtual font.
Thus if the local font is to be used at the same size
as the design size of the virtual font itself, |s| will be the
integer value $2^{20}$. The value of |s| must be positive and less than
$2^{24}$ (thus less than 16 when considered as a |fix_word|).
The dimension~|d| is a |fix_word| in units of printer's points; hence it
is identical to the design size found in the corresponding \.{TFM} file.

@d vf_id_byte=202

@<Glob...@>=
@!vf_file:byte_file;

@ The preamble is followed by zero or more character packets, where each
character packet begins with a byte that is $<243$. Character packets have
two formats, one long and one short:

\yskip\hang|long_char| 242 |pl[4]| |cc[4]| |tfm[4]| |dvi[pl]|. This long form
specifies a virtual character in the general case.

\yskip\hang|short_char0..short_char241|
|pl[1]| |cc[1]| |tfm[3]| |dvi[pl]|. This short form specifies a
virtual character in the common case
when |0<=pl<242| and |0<=cc<256| and $0\le|tfm|<2^{24}$.

\yskip\noindent
Here |pl| denotes the packet length following the |tfm| value; |cc| is
the character code; and |tfm| is the character width copied from the
\.{TFM} file for this virtual font. There should be at most one character
packet having any given |cc| code.

The |dvi| bytes are a sequence of complete \.{DVI} commands, properly
nested with respect to |push| and |pop|. All \.{DVI} operations are
permitted except |bop|, |eop|, and commands with opcodes |>=243|.
Font selection commands (|fnt_num0| through |fnt4|) must refer to fonts
defined in the preamble.

Dimensions that appear in the \.{DVI} instructions are analogous to
|fix_word| quantities; i.e., they are integer multiples of $2^{-20}$ times
the design size of the virtual font. For example, if the virtual font
has design size $10\,$pt, the \.{DVI} command to move down $5\,$pt
would be a \\{down} instruction with parameter $2^{19}$. The virtual font
itself might be used at a different size, say $12\,$pt; then that
\\{down} instruction would move down $6\,$pt instead. Each dimension
must be less than $2^{24}$ in absolute value.

Device drivers processing \.{VF} files treat the sequences of |dvi| bytes
as subroutines or macros, implicitly enclosing them with |push| and |pop|.
Each subroutine begins with |w=x=y=z=0|, and with current font~|f| the
number of the first-defined in the preamble (undefined if there's no
such font). After the |dvi| commands have been
performed, the |h| and~|v| position registers of \.{DVI} format and the
current font~|f| are restored to their former values;
then, if the subroutine has been invoked by a \\{set\_char} or \\{set}
command, |h|~is increased by the \.{TFM} width
(properly scaled)---just as if a simple character had been typeset.

(The following commands, already defined as macros with respect to the
\.{DVI} file, have the same interpretation when they appear in a virtual
font file: \hang |set_char_0|, |set1|, |set_rule|, |put1|, |put_rule|, |nop|,
|push|, |pop|, |right1|, |w0|, |w1|, |x0|, |x1|, |down1|, |y0|, |y1|, |z0|,
|z1|, |fnt_num_0|, |fnt1|, |xxx1|, |xxx4|, |fnt_def1|, |pre|, |post|.)

@d long_char=242 {\.{VF} command for general character packet}
@d improper_DVI_for_VF==139,140,243,244,245,246,247,248,249,250,251,252,
    253,254,255

@ The character packets are followed by a trivial postamble, consisting of
one or more bytes all equal to |post| (248). The total number of bytes
in the file should be a multiple of~4.

@ Here are some functions, analogous to those for the |dvi_file|, for
reading one or more bytes from the |vf_file|.

Note that variable |flushing| is used to suppress the warning (when |eof| is
met on the |vf_file|) if bytes are merely being read to discard the
postamble.

@p procedure read_vf_file(var x:eight_bits);
  label 9999;
  begin while vf_count >= VAX_block_length do
    begin get(vf_file,VAX_continue); vf_count:=vf_count-VAX_block_length;
      incr(vf_block);
      if eof(vf_file) then
      begin if not flushing then
          warning('VF file ended without postamble!');
@:Warning: VF file ended without postamble}{\quad\.{VF file ended without postamble}@>
        x := post; goto 9999
      end
    end;
    x := vf_file^[vf_count];
    incr(vf_count); incr(vf_loc);
9999:
  end;
@#
function @!vf_one: integer;  {returns the next byte, unsigned}
  var @!b: eight_bits;
begin
    read_vf_file(b); vf_one:=b
end;
@#
function vf_byte:integer; {returns the next byte, signed}
var b:eight_bits;
begin read_vf_file(b);
if b<128 then vf_byte:=b @+ else vf_byte:=b-256;
end;
@#
function vf_two:integer; {returns the next two bytes, unsigned}
var a,@!b:eight_bits;
begin read_vf_file(a); read_vf_file(b);
vf_two:=a*256+b;
end;
@#
function vf_pair:integer; {returns the next two bytes, signed}
var a,@!b:eight_bits;
begin read_vf_file(a); read_vf_file(b);
if a<128 then vf_pair:=a*256+b
else vf_pair:=(a-256)*256+b;
end;
@#
function vf_three:integer; {returns the next three bytes, unsigned}
var a,@!b,@!c:eight_bits;
begin read_vf_file(a); read_vf_file(b); read_vf_file(c);
vf_three:=(a*256+b)*256+c;
end;
@#
function vf_trio:integer; {returns the next three bytes, signed}
var a,@!b,@!c:eight_bits;
begin read_vf_file(a); read_vf_file(b); read_vf_file(c);
if a<128 then vf_trio:=(a*256+b)*256+c
else vf_trio:=((a-256)*256+b)*256+c;
end;
@#
function vf_quad:integer; {returns the next four bytes, signed}
var a,@!b,@!c,@!d:eight_bits;
begin read_vf_file(a); read_vf_file(b); read_vf_file(c); read_vf_file(d);
if a<128 then vf_quad:=((a*256+b)*256+c)*256+d
else vf_quad:=(((a-256)*256+b)*256+c)*256+d;
end;

@ Here are the globals required to manipulate the VF file.

@<Glob...@>=
@!vf_count, @!vf_block, @!vf_loc : integer;
@!flushing : boolean;

@*Handling Virtual Fonts. Before we look around the fonts, we'd better see
if any of the fonts so far loaded are virtual ones; this can be determined
by whether a \.{VF} file exists for the given font.

We use a |while| loop, because the analysis of a virtual font most probably
will result in the addition of one or more new fonts, some of which may
themselves be virtual; to prevent infinite recursion with a self-referential
font, such additional fonts are only loaded if glyphs within the font are
activated.

@<Remap any virtual fonts@>=
TeX_font:=0; vf_ptr:=0;
while TeX_font < nf do  {Currently defined fonts, and any this defines anew}
begin
if font_type[TeX_font]=wanted then
  @<Locate virtual font metrics@>;
incr(TeX_font)
end;

@ This module determines the name of the \.{VF} file, if it exists,
corresponding to the current \TeX\ font.

@<Locate virtual font metrics@>=
if not no_virt_support then
begin
  file_name:='';
  for n:=font_name[TeX_font] to font_name[TeX_font+1]-1 do
    if (names[n]>="a") and (names[n]<="z") then
      file_name:=file_name+xchr[names[n]+"A"-"a"]
    else
      file_name:=file_name+xchr[names[n]];
  open(vf_file,file_name,@=readonly@>,@=access_method@>:=@=direct@>,
       @=user_action@>:=file_open,@=default@>:=tex_virtual+'.VF;0',
       VAX_continue);
  file_stat:=status(vf_file);
  if (file_stat<>0) and (file_stat<>3) then
  begin
    case file_stat of
      2: file_message := 'PAS-E-ERRDUROPE';
      5: file_message := 'PAS-E-ACCMETINC';
      6: file_message := 'PAS-E-RECLENINC';
      7: file_message := 'PAS-E-RECTYPINC';
      8: file_message := 'PAS-E-ORDSPEINC';
    othercases
      file_message := str_int(file_stat);
    endcases;
    abort('Couldn''t open virtual font ',file_name,'. Status=',file_message);
@:fatal error Couldn't open virtual font}{\quad\.{Couldn't open virtual font}@>
  end
  else if file_stat=0 then
  begin
    reset(vf_file);
    vf_block:=0; vf_count:=0; vf_loc:=0;
    font_type[TeX_font]:=virtual;
    open_file_name:=def_file_name;@/
    @<Find name and magnification of font file@>;@/
    @<Report name of font file in use@>;@/
    print_ln(' [virtual]');
    write_ln(term_out,' is virtual',crlf);@/
    read_VF;@/
    close(vf_file,@=disposition@>:=@=save@>,VAX_continue);
  end;
end

@ Globals for storing virtual font files: unlike \.{VFtoVP},
@.VFtoVP@>
we don't read the \\{entire} file into memory.  We do, however, store those
|dvi| bytes that belong to some wanted character packets, but only for
glyphs which are more complex than simply imaging a character from some
other font; this is efficient, and also feasible, since VF files are never
very large (unlike physical font files!) Much of this code is, once again,
lifted from \.{VFtoVP}.

The array |vf| is global, along with the pointer into it, |vf_ptr|, because
we accumulate therein all setting sequences read from all virtual font files
processed.  Most of the other variables are only required when actually
reading a particular virtual font, so are local to |read_VF|.

\yskip\hang |vf_take| When |dvi| bytes are to be ``read'' from array |vf|, the
variable |vf_take| will be non-negative and hence index an element of the
array; during normal input from the \.{DVI} file, a negative value ensures
that input is taken from the file itself.

\yskip\hang |TeX_font| will always point to the current virtual font being
processed, amongst all the fonts (both virtual and physical) that are in
the usual data structures.  This global variable has the same name as
parameters of the procedures |name_pxl_file| and |add_txf_to_lnf|, in order
that common sections of code may be shared from the W{\mc EB}.

\yskip\hang |font_type| records whether a font is |virtual| or physical (in which
case the value |wanted| will have been set when the font's \.{TFM} file was
first read.

@<Glob...@>=
@!vf:packed array[0..vf_size] of eight_bits; {the \.{VF} input data goes here}
@!vf_ptr:0..vf_size; {first unused location in |vf|}
@!vf_take:-1..vf_size; {next byte to be ``read'' from |vf|}
@!TeX_font:0..max_fonts; {index into font metrics database for current virtual}
@!font_type: array [0..max_fonts] of download_status;

@ To ensure that we start by reading genuine |dvi| bytes from the \.{DVI}
file, we must make |vf_take| negative.

@<Preset...@>==
vf_take:=-1;

@ Here's the main procedure for reading virtual font files.  It exists as a
procedure purely in order that local variable declarations can be made to
cater for those code sections which are also invoked within |define_font|.

When we encounter a font definition in the \.{VF} file, we read its
associated \.{TFM} file in the usual fashion; we associate a ``\TeX\ font
number'' with this font, determined from the highest font previously used
within the previous actual or virtual fonts, via the variable |font_base|.

@p procedure read_VF;
var @!f: 0..max_fonts; {Local index}
  @!p: integer; {length of the area/directory spec}
  @!n: integer; {length of the font name proper}
  @!c,@!q,@!d: integer; {check sum, scaled size, design size}
  @!r: 0..name_length; {index into |cur_name|}
  @!j,@!k: 0..name_size; {indices into |names|}
  @!mismatch: boolean; {names differ}
  @!font_base: 0..max_fonts; {offset for local font numbers}
  @!z,@!alpha,@!beta: integer; {to facilitate |fix_word| multiplications}
  @!font_ref:array[0..max_fonts] of integer; {local font numbers}
  @!font_ext:array[0..max_fonts] of integer; {``external'' font numbers}
  @!font_ptr:0..max_fonts; {number of local fonts}
  @!packet_start:array[0..255] of 0..vf_size; {character packet boundaries}
  @!packet_found:boolean; {at least one packet has appeared}
  @!temp_byte:eight_bits;@+@!temp_int:integer; {registers for simple calculations}
  @!pl:integer; {packet length}
  @<Declare |read_VF|'s local procedures@>@;
begin
  font_base:=highest_font+1; flushing:=false;@/
  @<Read the whole \.{VF} file@>;
end;

@ Again we cautiously verify that we've been given decent data.

@d vf_abort(#)==
  abort('(VF file) ',#,crlf)
@!@:fatal error VF file ...}{\quad\.{(VF file)}@>

@<Read the whole \.{VF} file@>=
temp_byte:=vf_one;
if temp_byte<>pre then vf_abort('First byte isn''t `pre''');
@:fatal error VF file First byte}{\qquad\.{First byte isn't \it pre}@>
@<Read the preamble command@>;
@<Read and store the font definitions and character packets@>;
@<Read and verify the postamble@>

@ Here's where we read the preamble of the virtual font file.  Note that we
make the assumption throughout that a `DVI unit' is \.{1sp} (as it always is
in output generated by \TeX), so that $\hbox{`DVI unit'}=16\hbox{\.{fix}}$.

@<Read the preamble command@>=
temp_byte:=vf_one;
if temp_byte<>vf_id_byte then vf_abort('Wrong version number in second byte!');
@:fatal error VF file Wrong VF version}{\qquad\.{Wrong VF version...}@>
temp_byte:=vf_one;  {read the length of introductory comment}
for k:=1 to temp_byte do temp_int:=vf_one; {and discard it}
temp_int:=vf_quad;
if (temp_int<>0) and (font_check_sum[TeX_font]<>0) and
   (temp_int<>font_check_sum[TeX_font]) then
  error('VF checksum disagreed! TFM:',font_check_sum[TeX_font]:1,
           ' VF:',temp_int:1);
@:Error: VF checksum disagreed}{\quad\.{VF checksum disagreed}@>
z:=vf_quad div 16; {convert to |dvi| units}
if z<>font_design_size[TeX_font] then
  error('VF design size disagreed! TFM:',font_design_size[TeX_font]:1,
           ' VF:',z:1);
@:Error: VF design size disagreed}{\quad\.{VF design size disagreed}@>
z:=font_scaled_size[TeX_font]; {but we're using it at this size}
@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;

@ The remainder of the file will consist of font definitions and character
packets; once any of the latter has been met, it is not legal for the former
to appear again.

@<Read and store the font definitions and character packets@>=
for k:=0 to 255 do packet_start[k]:=vf_size;
font_ptr:=0; packet_found:=false;
repeat temp_byte:=vf_one;
  if temp_byte<>post then
    if temp_byte>long_char then @<Read and store a font definition@>
    else @<Read and store a character packet@>;
until temp_byte=post

@ The postamble of the virtual font file should consist purely of |post|
bytes; furthermore, the file should be a multiple of four bytes long.

@<Read and verify the postamble@>=
flushing:=true;
while (temp_byte=post)and not eof(vf_file) do temp_byte:=vf_one;
if not eof(vf_file) then
  warning('extra junk at end of the VF file.');
@:Warning: extra junk at end of VF file}{\quad\.{extra junk...VF file}@>
if vf_loc mod 4 <> 0 then
  warning('VF data not a multiple of 4 bytes')
@:Warning: VF data not a multiple of 4 bytes}{\quad\.{VF data not a multiple of 4 bytes}@>

@ The font definition reads as follows:

\yskip\hang|fnt_defN| 243..6 |k[1..4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.

\noindent where |k| is a local font number; this is used as an offset from
|font_base| when reading the associated \.{TFM} file.

@<Read and store a font definition@>=
begin if packet_found or(temp_byte>=pre) then
  vf_abort('Illegal byte ',temp_byte:1,' at start of character packet!');
@:fatal error VF file Illegal byte}{\qquad\.{Illegal byte...at start...}@>
case temp_byte of
 fnt_def1: font_ref[font_ptr]:=vf_one;
 fnt_def1+1: font_ref[font_ptr]:=vf_two;
 fnt_def1+2: font_ref[font_ptr]:=vf_three;
 fnt_def1+3: font_ref[font_ptr]:=vf_quad {there are no other cases}
endcases;@/
font_ext[font_ptr]:=font_base+font_ptr; {invent ``external'' font number}
if font_ptr=max_fonts then vf_abort('References too many fonts!');
@:fatal error VF file References}{\qquad\.{References too many fonts}@>
@<Move the font parameters into position for font |nf|,
      and print the font name@>;
print(' (local font ',font_ref[font_ptr]:1,' of font ',font_num[TeX_font]:1,')');
if highest_font<font_ext[font_ptr] then
  highest_font:=font_ext[font_ptr];
@<Avoid reloading the font metrics...@>;
if f<nf then
begin font_ext[font_ptr] := font_num[f];
  print_ln('---is identical to font ',font_num[f]:1);
  incr(nf)
end else
  @<Read the local font's \.{TFM} file and record the characters it contains@>;
incr(font_ptr);
end

@ We need to position some of this information in a similar fashion to when
\.{TFM} files are read during normal font definitions from the \.{DVI} file:

@<Move the font parameters into position for font |nf|,
    and print the font name@>=
if nf = max_fonts then
  capacity_exceeded('too many fonts (max fonts=',max_fonts:1,')');
@:capacity exceeded too many fonts}{\quad\.{too many fonts}@>
font_num[nf]:=font_ext[font_ptr]; {record invented ``external'' font number}
font_map[nf]:=nf; {until we know better, this font is new}
c:=vf_quad; font_check_sum[nf]:=c; {|c[4]|}
q:=vf_quad; font_scaled_size[nf]:=q; {|s[4]|}
if (q<0) or (q>@'77777777) then {|s| is negative or exceeds $2^{24}-1$}
  vf_abort('Mapped font size is too big!');
@:fatal error VF file Mapped font size too big}{\qquad\.{Mapped font size too big}@>
d:=vf_quad div 16; font_design_size[nf]:=d; {|d[4]| in sp}
@<Determine actual scaling for physical font@>;
font_scaled_size[nf]:=q; {store revised scaling}
p:=vf_one; {|a[1]|}
n:=vf_one; {|l[1]|}
if font_name[nf]+n+p>name_size then
  capacity_exceeded('too many names (name size=',name_size:1,')');
@:capacity exceeded too many names}{\quad\.{too many names}@>
font_name[nf+1]:=font_name[nf]+n+p;
print('Font ',font_num[nf]:1,': ');
if n+p=0 then print('null font name!')
@.null font name@>
else
  for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=lower(vf_one);
incr(nf); print_font(nf-1); decr(nf); f:=nf;

@ When a |fnt_defN| is read from a virtual font file, the parameter |s| (in
variable |q|)  represents the scaled size relative to the design size of the
virtual font. The latter was stored (in |font_design_size[TeX_font]|; scaled
by $2^{-4}$) when the original definition of the virtual font was read from
the \.{DVI} file, and has been checked during the reading of the virtual
font's preamble.  Therefore, we will later call |in_TFM| to read the \.{TFM}
file for the physical font and adjust all dimensions by the appropriate
amount.  To do this, we have to compute a new |q| by performing ``correct''
arithmetic on the expression $\hbox{|font_design_size[TeX_font]|}\times q$.

We utilize the same algorithm as is used for converting the width information
in a \.{TFM} file; local variable |z| has already been set to the virtual
font's design size, and converted to $|z|^\prime$, so we just need to split up
the value |s| into its constituent bytes and perform the multiplications.

By applying a cast to treat |q| as |unsigned|, we ensure that |b0<>0| for
negative values of |q|.

@d VAX_unsigned_type == unsigned

@<Determine actual scaling for physical font@>=
b0:=(q::VAX_unsigned_type div @'100000000)::eight_bits;
b1:=(q mod @'100000000) div @'200000;
b2:=(q mod @'200000) div @'400; b3:=q mod @'400;
q:=(((((b3*z) div @'400)+(b2*z)) div @'400)+(b1*z))div beta;
if b0>0 then q:=q-alpha; {assume $|q|<2^{25}$}


@ @<Read the local font's \.{TFM} file...@>=
begin
  @<Move font name into the |cur_name| string@>;
  open_tfm_file;
  font_bc[nf]:=0; font_ec[nf]:=256; {will cause error if not modified soon}
  if in_TFM(q) then @<Finish loading the new font info@>;
  if font_bc[nf]<=font_ec[nf] then
    if font_ec[nf]>255 then abort('---not loaded, bad TFM file!');
@:fatal error Font not loaded Bad TFM file}{\qquad\.{Bad TFM file}@>
  print_ln('')
end

@ Here is a procedure for ``outputting'' a |dvi| sequence by storing it in
|vf|; each command is output in the minimum number of bytes.

We cheat and abuse VAX-\PASCAL's variant records to provide a simple
conversion between integers (signed or unsigned) and bytes in bigendian
order.  Variable |byte_ct| determines how many bytes are actually required.

Parameter |code| is the command code being output; this will be incremented
(by |1..3|) if |2..4| parameter bytes are necessary.  |param| is obviously
the parameter for |code|; |signed| is negative if |param| is to be
interpreted in twos-complement notation.  Otherwise, |signed=0|, except for
|set_rule| and |put_rule| (which require that all four bytes are output for
the parameter) when |signed=1|.  For output of the second parameter of these
latter two commands, |code=0|, which suppresses output of the |code| byte.

The following definition is used for any bytes which must be copied to array
|vf|.

@d vf_store(#)==begin vf[vf_ptr]:=#; incr(vf_ptr) end

@<Declare |read_VF|'s local procedures@>=
procedure vf_to_dvi(@!code:eight_bits;@!param:integer;@!signed:integer);
  var @!byte_ct:0..3; {offset to be added to |code|}
      @!cheat:packed record
                case boolean of
                  false: (b0,b1,b2,b3:eight_bits);
                  true:  (fullword:integer)
              end;
begin
  cheat.fullword:=param; byte_ct:=3;    {assume needs 4-byte parameter}
  if signed<=0 then
  with cheat do
  if ((b3=0) and ((signed=0) or (b2<128)))
    or ((b3=255) and (signed<0) and (b2>=128)) then
  begin
    byte_ct:=2; {if |b3| superfluous, store 3-byte parameter}
    if ((b2=0) and ((signed=0) or (b1<128)))
      or ((b2=255) and (signed<0) and (b1>=128)) then
    begin
      byte_ct:=1; {if |b3| and |b2| superfluous, store two bytes}
      if ((b1=0) and ((signed=0) or (b0<128)))
        or ((b1=255) and (signed<0) and (b0>=128)) then
        byte_ct:=0; {|b3|, |b2| and |b1| all superfluous; one byte parameter}
    end
  end;
  if code<>0 then
    vf_store(code+byte_ct); {store the appropriate command byte}
  if byte_ct>2 then vf_store(cheat.b3);
  if byte_ct>1 then vf_store(cheat.b2);
  if byte_ct>0 then vf_store(cheat.b1);
  vf_store(cheat.b0);
end;

@ This function computes how many bytes form the parameters of a command
taken from a character packet in the \.{VF} file; in the case of any special
sequences amongst these bytes (|xxx1..xxx4|), we read and discard all the
bytes here, since we don't support such sequences in virtual fonts.  The
number of bytes thus discarded is returned, negated.

Any occurrence of a command which is illegal in a character packet causes
the font to be rejected.

@<Declare |read_VF|'s local procedures@>=
function vf_skip( @!o:eight_bits) : integer;
  var @!temp : integer;
begin
  case o of
    sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64),
    w0,x0,y0,push,pop,sixty_four_cases(fnt_num_0):
      vf_skip:=0;
    set1,put1,fnt1,right1,w1,x1,down1,y1,z1:
      vf_skip:=1;
    set1+1,put1+1,fnt1+1,right1+1,w1+1,x1+1,down1+1,y1+1,z1+1:
      vf_skip:=2;
    set1+2,put1+2,fnt1+2,right1+2,w1+2,x1+2,down1+2,y1+2,z1+2:
      vf_skip:=3;
    set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,
    y1+3,z1+3,fnt1+3:
      vf_skip:=4;
    xxx1: begin temp:=vf_one; vf_skip:=-1-temp @+end;
    xxx1+1: begin temp:=vf_two; vf_skip:=-2-temp @+end;
    xxx1+2: begin temp:=vf_three; vf_skip:=-3-temp @+end;
    xxx1+3: begin temp:=vf_quad; vf_skip:=-4-temp @+end;
    improper_DVI_for_VF:
      vf_abort('contains illegal command in character packet');
@:fatal error VF file contains illegal command}{\qquad\.{contains illegal command...}@>
  endcases;
  if (o>=xxx1) and (o-xxx1<4) then
  begin
    decr(vf_ptr); {``unstore'' the |xxx| command byte}
    while temp>0 do
    begin o:=vf_one; decr(temp) end  {discard |xxx| sequence bytes}
  end
end;

@ This procedure adds to the saved packet a command to select a font; this
will be an pseudo external font number, converted from the local font number
used when a physical font is mapped into the virtual one.  The procedure has
the side-effect of setting its second parameter to index the stored metrics
of the newly-selected font.

@<Declare |read_VF|'s local procedures@>=
procedure store_fnt(@!num:integer; var @!f:integer);
var@!k:0..max_fonts;
begin
  f:=-1; {this value allows us to detect lack of the font}
  for k:=0 to font_ptr-1 do {scan the local font reference numbers}
    if font_ref[k]=num then f:=k; {find its entry}
  if f<0 then
    vf_abort('packet ',c:1,'invokes unmapped font ',num:1);
@:fatal error VF file packet invokes}{\qquad\.{packet invokes unmapped font}@>
  num:=font_ext[f]; {convert to pseudo external number}
  f:=0; while font_num[f]<>num do incr(f);
  if num < 64 then vf_store(fnt_num_0+num) else
    vf_to_dvi(fnt1,num,0); {|fntN k[N]|}
end;

@ This procedure adds to the saved packet a command to set a character; it
is required because we have to read all the bytes of such a command (in
order to determine to what character a packet is being mapped).

@<Declare |read_VF|'s local procedures@>=
procedure store_char(@!cc:integer);
begin
  if cc < 128 then vf_store(set_char_0+cc) else
    vf_to_dvi(set1,cc,0); {|setN c[N]|}
end;

@ When a character packet is being read from the virtual font file, we utilize
this procedure to store the bytes, translating any font selection commands to
reference the pseudo external fonts that have been created corresponding to
each local font of the virtual font file.

The packet taken from the \.{VF} file is surrounded by a |push|/|pop| pair
to maintain the program's conception of the current position.   We start by
assuming local font $0$, which is that loaded as pseudo external font
|font_base|.

After the packet has been copied, an |eop| is inserted into the stored
packet to permit detection of the end when the packet is later ``played
back''.

@<Declare |read_VF|'s local procedures@>=
procedure vf_copy(@!pl,@!cc : integer);
  var @!f:-1..max_fonts; {index into physical font data structures}
      @!param_len:integer; {count of parameter bytes}
      @!local_font:integer; {index into local font data structures}
      @!mapped_char:integer; {index into a locally referenced font}
      @!command:eight_bits; {next |dvi| command in \.{VF} file}
      @!simple_packet, {packet contains only one |setN|, with maybe one |fntN|}
      @!one_set,@!one_fnt:boolean; {|false| if more than one of each}
      @!start_ptr,seq_ptr:0..vf_size; {pointers into bytes stored in |vf|}
      @!scaling_required:boolean; {dimension needs revision before storing}
begin
  if vf_ptr+pl>=vf_size then capacity_exceeded('too many virtual characters');
@:capacity exceeded too many virt}{\quad\.{too many virtual characters}@>
  start_ptr:=vf_ptr; vf_store(push);
  store_fnt(0,f);
  glyph_map(TeX_font)(cc).font_code:=f;
  one_set:=false; one_fnt:=false; simple_packet:=true; seq_ptr:=vf_ptr;
  while pl>0 do
  begin
    command:=vf_one;
    vf_store(command);
    decr(pl);
    param_len:=vf_skip(command);
    @<Act upon any font changing command@>;
    @<Act upon any character setting command@>;
    @<Act upon commands that require scaling of parameters@>@;
    case command of
      sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64),
      four_cases(set1): one_set:=true;
      sixty_four_cases(fnt_num_0),four_cases(fnt1): one_fnt:=true;
      set_rule, put_rule: {these have \\{two} 4-byte parameters}
        begin param_len:=param_len-4; simple_packet:=false
      end;
    othercases
      simple_packet:=false;
    endcases;
    if param_len<0 then {|abs(param_len)| bytes already copied}
      pl:=pl+param_len
    else
      while param_len>0 do {copy remaining bytes of |command|}
        begin command:=vf_one; vf_store(command);
          decr(pl); decr(param_len)
        end
  end;
  vf_store(pop);
  vf_store(eop);
  if simple_packet then vf_ptr:=start_ptr {discard the packet}
                   else with glyph_map(TeX_font)(cc) do
                   begin loaded:=virtual; {indicate where \AM\ how to find it}
                    seq_off:=start_ptr
                   end
end;

@ If a character packet contains any command to select a font, the command
must be expanded, and the local font number converted into a reference to
the appropriate pseudo external font.  Since this may have the effect of
reading some bytes from the packet, we reduce our goal by the appropriate
quantity.

If this is the first font changing command in the packet, the internal font
it defines is placed into the current character's |glyph_map| entry, to
permit later overwriting with the entry for the physical character.  If,
moreover, it is the first command \\{stored} in the packet, apart from the
initial |push| and the invocation of local font $0$, we can over-write the
latter.

@<Act upon any font changing command@>=
case command of
  sixty_four_cases(fnt_num_0): local_font:=command-fnt_num_0;
  fnt1: local_font:=vf_one;
  fnt1+1: local_font:=vf_two;
  fnt1+2: local_font:=vf_three;
  fnt1+3: local_font:=vf_quad;
othercases
  do_nothing
endcases;
if (command>=fnt_num_0) and (command<=fnt1+3) then
begin decr(vf_ptr); {``unstore'' the command byte}
  store_fnt(local_font,f);
  param_len := -param_len; {indicate bytes already copied}
  if vf_ptr=seq_ptr then {we can overwrite the `|fnt_num_0|'}
    vf_ptr:=start_ptr+1; {by writing immediately after the |push|}
  if not one_fnt then glyph_map(TeX_font)(cc).font_code:=f
                 else simple_packet:=false
end;

@ Character packets usually contain one or more commands to set a character;
this code reads all necessary bytes to determine the code for the character
being set.  Note that this code is \&{not} activated for commands to \\{put}
characters, since these usually appear only in more complex character
packets.  If no previous character setting command has been met, then the
character code is written into the |glyph_map|.

@<Act upon any character setting command@>=
case command of
  sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
    mapped_char:=command-set_char_0;
  set1: mapped_char:=vf_one;
  set1+1: mapped_char:=vf_two;
  set1+2: mapped_char:=vf_three;
  set1+3: mapped_char:=vf_quad;
othercases
  do_nothing
endcases;
if (command>=set_char_0) and (command<=set1+3) then
begin decr(vf_ptr); {``unstore'' the command byte previously copied}
  store_char(mapped_char); {store a new character setting sequence}
  param_len:=-param_len; {indicate this many bytes already consumed}
  with glyph_map(f)(mapped_char) do
    if loaded=unused then loaded:=wanted;
  if not one_set then glyph_map(TeX_font)(cc).char_code:=mapped_char
                 else simple_packet:=false;
  if font_type[f]=unused then font_type[f]:=wanted;
end;

@ Many other |dvi| commands within a character packet require that the
parameters be read, because they must be scaled by the size at which the
virtual font is used.  We start by assuming that the |dvi| bytes \&{will} be
read from the \.{VF} file, and thus negate |param_len| so that the main loop
will know that those bytes have been read.  If we \&{don't} read the
command's parameters, |param_len| will be negated back again, and thus lead
to that number of bytes being copied verbatim from the \.{VF} file to |vf|.

@<Act upon commands that require scaling of parameters@>==
param_len:=-param_len;
scaling_required:=false;
case command of
set_rule,put_rule:
  begin q:=vf_quad;  {read height of rule}
    decr(vf_ptr); {``unstore'' command byte}
    @<Determine actual scaling for physical font@>;
    vf_to_dvi(command-3,q,1);   {store command and first parameter}
    q:=vf_quad; {read width of rule}
    param_len:=-8; {we've consumed 8 bytes from |pl|}
    @<Determine actual scaling for physical font@>;
    vf_to_dvi(0,q,1) {store second parameter}
  end;
right1,w1,x1,down1,y1,z1:
  begin q:=vf_byte; scaling_required:=true end;
right1+1,w1+1,x1+1,down1+1,y1+1,z1+1:
  begin q:=vf_pair; scaling_required:=true end;
right1+2,w1+2,x1+2,down1+2,y1+2,z1+2:
  begin q:=vf_trio; scaling_required:=true end;
right1+3,w1+3,x1+3,down1+3,y1+3,z1+3:
  begin q:=vf_quad; scaling_required:=true end;
othercases
  param_len:=-param_len
endcases;
if scaling_required then
begin
    decr(vf_ptr); {``unstore'' the command byte}
    @<Determine actual scaling for physical font@>;
    vf_to_dvi(command+param_len+1,q,-1) {store command again, with revised dimension}
end;

@ When a character packet is found, we only bother with it if it defines a
character that's used in the document being processed; other character
packets are merely skipped.

If the packet consists merely of commands to set a single character from one
of the physical fonts to which the virtual font is mapped, then we can
simply alter the information in the |glyph_map| to redirect requests to the
appropriate character of the physical font; the |glyph_map| of that font is
in turn marked to indicate that the character is used in the current
document.

On the other hand, if the character packet requires more complex positioning
commands, the whole packet is stored in array |vf|; the starting position of
the sequence is recorded in field |seq_off| of the |glyph_map| entry, whilst
the |loaded| field is given the value |virtual|.  Field |font_code| will
contain the internal font number assigned to local font zero of the virtual
font, whereas the font selection commands stored in |vf| use the pseudo
external font numbers, since these bytes will require interpretation by the
normal dvi-reading routines.

@<Other values of |download_status|@>==,@!virtual

@ @<Other fields of |glyph_info|@>==@!seq_off : integer;

@ The \.{VF} format supports arbitrary 4-byte character codes, but \.{VPL}
format presently does not.

Since the |tfm| widths in the character packets are unscaled, it would be
difficult to ensure that they correspond with those determined when the
virtual font's metrics were read, so they will be ignored at this time.

@d nonexistent(#)==((#<font_bc[TeX_font])or(#>font_ec[TeX_font])or
                    (char_width(TeX_font)(#)=invalid_width))

@<Read and store a character packet@>=
begin if temp_byte=long_char then
  begin pl:=vf_quad; c:=vf_quad; temp_int:=vf_quad;
    {|pl[4]| |cc[4]| |tfm[4]|}
  end
else begin pl:=temp_byte; c:=vf_one; temp_int:=vf_trio;
    {|pl[1]| |cc[1]| |tfm[3]|}
  end;
if nonexistent(c) then vf_abort('Character ',c:1,' does not exist!');
@:fatal error VF file Character c does not exist}{\qquad\.{Character does not exist}@>
if packet_start[c]<vf_size then
  warning('Discarding earlier packet for character ',c:1);
@:Warning: Discarding earlier packet}{\quad\.{Discarding earlier packet...}@>
if pl<0 then vf_abort('Negative packet length!');
@:fatal error VF file Negative packet length}{\qquad\.{Negative packet length}@>
packet_start[c]:=vf_ptr;
if glyph_map(TeX_font)(c).loaded <> unused then
  vf_copy(pl,c)
else
  while pl>0 do {discard unwanted character packet}
  begin temp_byte:=vf_one;
    decr(pl)
  end;
packet_found:=true;
end

@ Once all characters from physical fonts have been downloaded (and hence
may be assumed to have been mapped to character positions in the LN03's
downloaded fonts), we can complete the |glyph_map| entries for any virtual
fonts that may be present; for simple characters, this just involves copying
the entry for the appropriate character in the physical font.

We start with the most recently loaded font, so that chains through a number
of virtual fonts will be completed correctly.

@<Complete mapping of virtual characters@>=
for TeX_font:=nf-1 downto 0 do
  if font_type[TeX_font]=virtual then {only process virtual fonts}
  begin
    for m:=first_glyph_index(TeX_font) to last_glyph_index(TeX_font) do
    {cover all possible characters}
      with glyphs[m] do
        if loaded=wanted then
          glyphs[m]:=glyph_map(font_code)(char_code)
        else if loaded=virtual then {first char in sequence from this \TeX\ font}
          if font_type[font_code]=wanted then {provided it's a physical font}
            font_code:=txf_to_lnf[font_code] {maps to this LN03 font}
  end;


@*Description of LN03 Font Files.  The font files downloaded conform to DEC
standard 180; the definitions given below have been derived by the author from
the description of this standard included in the ``Font File Format Manual'',
Version 1, Variant 0, available on request from DEC.

Although we refer herein to font `files' they actually are always considered
to reside in memory, either that available in the basic printer, or extra
memory provided by plug-in RAM cartridges; presumably the same format is also
used to encode the built-in fonts, and those available in plug-in ROM
cartridges.

A font file is divided into ten regions; all data items may be referenced
\\{via} their offsets within one of these regions, which are predeterminable
for any particular combination of characters included in the file.  All
multi-byte quantities in a font file are stored in Vax/VMS standard form, with
the least-significant byte being at the \&{lower} address; this is the
opposite to the normal `Bigendian' order of most \TeX ware.

Since it is necessary to build the entire font file in memory before it can be
included in the \.{LN3} file for downloading to
the printer, we allocate a large array of memory sufficiently large to store
the rasters for the largest imaginable font file; the definition of
|lnf_bufsize| probably exceeds the maximum amount of memory available on the
printer by a comfortable margin!  This program refers to this `file' in memory
as the \.{LNF} file, although it is never written to a file as such; if this
program is adapted for use on a computer without virtual memory, it might
@^system dependencies@>
be necessary to utilize a random-access file to build the |lnf_file|.

Since the partially-built LN03 font file must be retained between successive
calls of |add_txf_to_lnf|, its data structures need to be declared globally.
Information in this ``file'' can consist of single bytes, unsigned (two-byte)
words, and signed (four-byte) longwords, as well as four-character strings, so
we define an |lnf_block| as a \PASCAL\ variant record so that this may be
(ab)used to permit the different methods of access.

We also declare here the |pxl_dir_type| which describes the 16-byte directory
record in a \.{PXL} file for any character's glyph.

@<Types...@>=
type @!lnf_block=packed record
        case integer of
        0: (@!bytes:packed array[0..511] of eight_bits);
        1: (@!words:packed array[0..255] of sixteen_bits);
        2: (@!longs:packed array [0..127] of integer);
        3: (@!str:packed array [0..127] of packed array [1..4] of char)
     end;

@ Many variables used by |add_txf_to_lnf| are declared globally, so that the
information contained therein is preserved between successive calls of the
function. |lnf| is the large memory-held `file' referred to above.

The variables |psize|, |lsize| and |msize| accumulate counts of raster bytes
generated in portrait, landscape and mixed modes (this program only creates
portrait mode rasters).  |ras_beg| and |ras_len| are used to form the
descriptor pointing to the Character Definition Region, whilst
|lnf_pool_offset| points to the string pool.  |lnf_len| keeps track of the
total size of the `file', whilst |using_GR| is set true when the |GR|
(right-hand half) of the font is being generated. (The LN03 supports use of
the |GL| and |GR| characters sets, corresponding to characters in the
(hexadecimal) range |@"21..@"7E| and |@"A1..@"FE|, respectively.)

The variable |file_stat| receives the exit status from VAX-Pascal's file
handling routines.

@<Globals...@>=
  @!lnf: array[0..lnf_bufsize] of lnf_block;
  @!psize,@!lsize,@!msize: integer;
  @!ras_beg,@!ras_len: integer;
  @!lnf_pool_offset,@!lnf_len: integer;
  @!using_GR: boolean;
  @!file_stat:integer;

@ The first region of an |lnf_file| is the file header.  This contains, as its
first longword (32-bit) quantity, a count of the total size of the file, in
bytes.  This is followed by the string |'FONT'|, to confirm that this is a DEC
Std 180 font.  This is followed immediately by a ``string descriptor''
pointing to the font-file identifier.  Font files contain many such descriptors;
each consists of two longwords, the first contains the string's length (in its
lower half), whilst the latter is the offset (relative to the start of the
file) of the first character of the string.

The font-file identifier descriptor is followed immediately (which is unusual)
by the characters of the identifier itself; for details of the content of this
string, read DEC Std 180.  The date and time of creation of the font follows;
this program arbitrarily sets these to 14:00:00 on 11-SEP-1973.

There then follow a number of descriptors pointing to the remaining regions of
the font file; these are, in order: |fnt_attr| font attributes region;
|fnt_params| font parameters region; |fnt_char_directory| font character
directory region; |fnt_seg_list| font segment list region; |fnt_future| font
future information region; |fnt_strings| font string pool region; |fnt_kerns|
font kerning information region; |fnt_chars| font character defintions region.

The header finishes with the character count information field, which contains
five longwords and three character locators (|fnt_first|$\to$|fnt_space|), the
organization flags, a count of the number of parameters used in each raster
definition and the raster expansion information field, which records the
number of characters defined in this file, and the number of bytes of rasters
required to define them; this field occupies a further 12 longwords
(|font_in_file_count|$\to$|fnt_mix_comp|).

@d fnt_total_size=0
@d fnt_identifier=fnt_total_size+4
@d fnt_version=fnt_identifier+4
@d fnt_file_id=@"14     {Where font identification appears}
@d fnt_date=@"58        {Where creation date starts}
@d fnt_attr=fnt_date+12 {Font attributes descriptor}
@d fnt_params=fnt_attr+8        {Font parameters descriptor}
@d fnt_char_directory=fnt_params+8      {Character directory descriptor}
@d fnt_seg_list=fnt_char_directory+8    {Font segment list descriptor}
@d fnt_future=fnt_seg_list+8    {Future info descriptor}
@d fnt_strings=fnt_future+8     {String pool descriptor}
@d fnt_kerns=fnt_strings+8      {Font kerning infor descriptor}
@d fnt_chars=fnt_kerns+8        {Character definitions descriptor}
@d fnt_first=fnt_chars+8        {First character in font}
@d fnt_last=fnt_first+4 {Last character in font}
@d fnt_less_than=fnt_last+4     {Locator for characters less then first}
@d fnt_greater_than=fnt_less_than+4     {Locator for characters greater than last}
@d fnt_error=fnt_greater_than+4 {Locator for the error character}
@d fnt_extension=fnt_error+4    {Extension count and use}
@d fnt_space=fnt_extension+8    {Space character code}
@d fnt_organisation=fnt_space+4 {Organisation flags}
@d fnt_no_of_params=fnt_organisation+4  {Number of character parameters}
@d fnt_in_file_count=fnt_no_of_params+4 {Number of locators in this file}
@d fnt_null_count=fnt_in_file_count+4   {Null locator count}
@d fnt_char_count=fnt_null_count+4      {Number of character definitions}
@d fnt_alt_blocks=fnt_char_count+4      {Number of alternate character blocks}
@d fnt_rasters=fnt_alt_blocks+4 {Number of rasters}
@d fnt_compressed=fnt_rasters+4 {Number of compressed rasters}
@d fnt_portrait=fnt_compressed+4        {Portrait mode byte count}
@d fnt_landscape=fnt_portrait+4 {Landscape byte count}
@d fnt_mixed=fnt_landscape+4    {``Mixed'' byte count}
@d fnt_port_comp=fnt_mixed+4    {Portrait compressed byte count}
@d fnt_land_comp=fnt_port_comp+4        {Landscape compressed byte count}
@d fnt_mix_comp=fnt_land_comp+4 {``Mixed'' compressed byte count}

@ The font attributes region always follows the header; items within this
region are referenced through their local offsets from the start of the
region.

The |fnt_attr_flags| carries 32 bits of font attribute information; these are
set arbitrarily by this program --- see the coding for details.

The character set designator (within the string pool) uniquely identifies the
character set contained within this font, and is accessed by the descriptor
|fnt_char_set|.  Similarly the |fnt_type_ID|, |fnt_type_family|, |fnt_font_ID|,
|fnt_type_cat| and |fnt_descr| descriptors access further strings in the
string pool.

Further fields in the font attributes region define numerical attributes of
the font (|fnt_type_size| thru |fnt_dev_char|).  The attributes region
concludes with a further two descriptors, pointing to strings giving the names
of the foundry and designer responsible for the font.

Numerical quantities referring to the dimensions of characters in DEC LN03
font files are commonly in ``points'' or ``centipoints'' (the latter obviously
being $1\over{100}$th of the former).  However, these are not true printer's
points (of which there are 72.27 to an inch); rather they are that horrible
compromise which imagines that there are exactly 72 points to an inch (and
which \TeX\ refers to as a \.{bp}).  Centipoints are also referred to as
`Gutenbergs', in tribute to the inventor of movable type.  It is quite
convenient for the LN03 (with 300 dots per inch) to work internally in
Gutenbergs, of which there are 7200 to an inch, since 24 Gutenbergs therefore
translate into a single pixel.

@d fnt_attributes=fnt_mix_comp+4        {Start of attributes region}@#

{Following define offsets within the Font Attributes Region of the ``file''}
@d fnt_attr_flags=0             {Flags e.g. roman}
@d fnt_char_set=fnt_attr_flags+4        {DESCR for character set designator}
@d fnt_designator_len=7         {Number of characters in set desginator string}
@d fnt_type_ID=fnt_char_set+8   {DESCR for Font Type Family ID}
@d fnt_type_ID_len=7            {Number of chars in Font Family ID string}
@d fnt_type_family=fnt_type_ID+8        {DESCR for Type Family Name}
@d fnt_type_family_len=16       {Number of chars in Type Family String}
@d fnt_font_ID=fnt_type_family+8        {DESCR for Font ID string}
@d fnt_font_ID_len=16           {Number of chars in Font ID string}
@d fnt_string_pool_len=fnt_designator_len+fnt_type_ID_len+fnt_type_family_len+fnt_font_ID_len
@d fnt_type_cat=fnt_font_ID+8   {DESCR for Type Category}
@d fnt_descr=fnt_type_cat+8     {DESCR for Font Description}
@d fnt_type_size=fnt_descr+8    {Font type size, in LN03 points}
@d fnt_average_width=fnt_type_size+4    {in centipoints}
@d fnt_resolution=fnt_average_width+4   {in $2^{-16}$ centipoints per pixel}
@d fnt_weight=fnt_resolution+4  {Coded 0..35. Normal is 16}
@d fnt_horiz_prop=fnt_weight+4  {0..35. Unexpanded/uncompressed is 16}
@d fnt_horiz_ratio=fnt_horiz_prop+4     {Ratio to a "normal" font}
@d fnt_pixel_aspect=fnt_horiz_ratio+4   {Ratio of X:Y of device's pixels}
@d fnt_rotation=fnt_pixel_aspect+4      {Ratio giving tangent of rotation}
@d fnt_dev_char=fnt_rotation+4  {Device specific characteristics}
@d fnt_foundry=fnt_dev_char+4   {DESCR of name of foundry}
@d fnt_designer=fnt_foundry+8   {DESCR of name of font designer}@#

@d fnt_attr_len=fnt_designer+8  {Length of Font Attributes Region of file}

@ The next region of the font file is the font parameters region.  All fields
of this region are referenced by means of their offsets within the region.
The first field is a 32-bit flags field, of which only two bits have defined
meanings.

The next field contains 8 items
(|fnt_underline_offset|$\to$|fnt_shadow_offset|) which define the position for
electronic over-, through- and under-lining, and the slant of the characters
and the delta offsets for emboldening.  None of these features are utilized by
DVItoLN03.

The next field (|fnt_superscript|$\to$|fnt_subscript|) describe the horizontal
and vertical offsets within the characters for the placement of super- and
sub-scripts.  The following field (|fnt_centreline|$\to$|fnt_digit_width|)
define various horizontal spacing parameters, whilst the final field of the
font parameters region (|fnt_topline_offset| thru |fnt_white_below|) define
vertical spacing parameters.

@d fnt_parameters=fnt_attributes+fnt_attr_len   {Start of Parameters Region}@#

{Following define offsets within the Font Parameters Region of the ``file''}
@d fnt_param_flags=0
@d fnt_underline_offset=fnt_param_flags+4       {All following in LN03 centipoints}
@d fnt_underline_thickness=fnt_underline_offset+4
@d fnt_thru_offset=fnt_underline_thickness+4    {Where strike-through line goes}
@d fnt_thru_thickness=fnt_thru_offset+4
@d fnt_overline_offset=fnt_thru_thickness+4
@d fnt_overline_thickness=fnt_overline_offset+4
@d fnt_slant=fnt_overline_thickness+4   {Ratio giving tan of slope from vertical}
@d fnt_shadow_offset=fnt_slant+4        {$\delta x$ and $\delta y$}
@d fnt_superscript=fnt_shadow_offset+4  {|y| and |x| offsets}
@d fnt_subscript=fnt_superscript+8
@d fnt_centreline=fnt_subscript+8       {Distance from left to centre widest char}
@d fnt_min_space=fnt_centreline+4       {Minimum acceptable width of interword space}
@d fnt_max_space=fnt_min_space+4
@d fnt_mean_space=fnt_max_space+4       {Width of space for monospace fonts}
@d fnt_em_width=fnt_mean_space+4        {Width of an |em| space}
@d fnt_en_width=fnt_em_width+4          {Width of an |en| space}
@d fnt_thin_space=fnt_en_width+4
@d fnt_digit_width=fnt_thin_space+4     {All digits usually same width}
@d fnt_topline_offset=fnt_digit_width+4 {From baseline to top of type size}
@d fnt_accent=fnt_topline_offset+4      {Where floating accents go}
@d fnt_halfline_offset=fnt_accent+4     {Where centre bar of an \.H appears}
@d fnt_overall_height=fnt_halfline_offset+4     {Sum of |height+depth|}
@d fnt_height=fnt_overall_height+4      {Includes ``leading'' above}
@d fnt_depth=fnt_height+4
@d fnt_H_height=fnt_depth+4
@d fnt_ex_height=fnt_H_height+4
@d fnt_white_above=fnt_ex_height+4
@d fnt_white_below=fnt_white_above+4@#

@d fnt_params_len=fnt_white_below+4     {Length of Font Parameters Region}

@ The next major region contains the character directory.

@d fnt_char_dir=fnt_parameters+fnt_params_len   {Where directory goes}

@ The next regions are: Table of Font Segments, Future Information, String
Pool and Kerning Information.  These are followed by the Character Definition
Region, which contains details of the rasters for each character's glyph.

Each character definition commences with |fnt_char_pars| of parameter
information; these are accessed through the offsets defined below.

In this version of the standard, each character is allocated 16 bytes of
parameter information.

@d fnt_char_pars=16     {Size of character parameters --- fixed}@#

@d fnt_kern_index=0             {Zero, or index into kerning table}
@d fnt_char_flags=fnt_kern_index+1      {Only 24 bits of flags}
@d fnt_char_width=fnt_char_flags+3      {In LN03 centipoints (or ``Gutenbergs'')}
@d fnt_left_bearing=fnt_char_width+4    {White space to left of raster}
@d fnt_raster_baseline=fnt_left_bearing+4  {Baseline relative to top of raster}

@ The character's parameters are followed by the encoded character raster:
this itself contains a number of counts preceding the rasters themselves; the
following definitions facilitate access to these fields.

@d fnt_char_orient=0            {0=Portrait, etc.}
@d fnt_char_type1=fnt_char_orient+1     {81H=Aligned expanded bit string}
@d fnt_char_type2=fnt_char_type1+1      {Unused if |fnt_char_type1| = 81H}
@d fnt_char_rows=fnt_char_orient+4      {Number of rows in raster}
@d fnt_char_cols=fnt_char_rows+2        {Number of columns in raster}
@d fnt_char_raster=fnt_char_cols+2      {Where rasters actually start}



@* Locating files containing character definitions. This procedure
determines the ``name'' of the required pixel file, and compares it with that
which is currently open, if any.  If these names differ, we close the currently
open file, if any, and open the new one. Although the file will have already
been opened and checked for validity during font file generation, we still
perform the checks, because that also sets certain pointers correctly.

This procedure is used by |print_glyph| to output a bitmap image of any
oversized rasters.  Note that it \&{has} parameter list |( TeX_font : integer)|;
this is omitted here because it has already been declared |forward|.

Note also that the variable |open_file_name| referenced is the \&{global}
variable of the same name, used by |print_glyph|, and not the identically
named formal parameter of |add_txf_to_lnf|.  The same name has to be used
because these \.{WEB} code sections incorporated are used in that function
also.

@p procedure change_pixel_file; {|change_pixel_file(TeX_font : integer);|}
  var @!new_file : file_spec;
    i: integer; {Used when reading the directory}
begin
  name_pxl_file(TeX_font,new_file,pxl_ident);
  if new_file<>open_file_name then
  begin
    if (open_file_name<>'') then
      close(pxl_file,@=disposition@>:=@=save@>,VAX_continue);
    open_file_name:=new_file;@/
    @<Open the file containing the glyph bitmaps@>;
   if pxl_ident=pixel_file then begin
       @<Check validity of PXL file@>;
       @<Read character directory from |pxl_file|@>
   end else begin
      @<Read preamble@>;
      skip_specials
   end
  end
end;

@* Creating the font file. The following function allocates the used
characters of the |TeX_font| to the predetermined locations in the |ln_font|;
the character rasters are taken from the LN03 font file |open_file_name|, which
has been set up to include the appropriate directory and file-name.  After each
font file has been constructed in memory, it is converted to sixel format (See
below) and added to the records in the \.{LN3} file |ln3_file|.

The use of each of the parameters and local variables is as follows:

\yskip\hang|ln_font| The serial number of the LN03 font being generated.
Should a font have to cross the boundaries between internal fonts, this will
be updated (it's not a |var| parameter, so won't upset anything in the
caller).  It is used in the procedure as a cross-check that the character
glyph currently being considered actually belongs to the font being created.


\yskip\hang|TeX_font| The identifying number of the \TeX\ font;
|font_num[TeX_font]| is that given by the \.{DVI} file.

\yskip\hang|open_file_name| Full file specification from which the rasters
should be read.

\yskip\hang|file_type| Indicates whether the |open_file_name| contains a
|packed_file| or a |pixel_file|.

\yskip\hang|rasters| Counts the number of bytes created in the current
|font_file|.  The variable is initialized to four times the number of
characters added since each character requires a four-byte character locator
in the font file directory region.

\yskip\hang|i| and |j| General loop counters.

Further local variables will be declared for the function as necessary.

@p function add_txf_to_lnf(ln_font,TeX_font:integer;
                open_file_name:file_spec;
                file_type:pixel_types):integer;
var  @<Local variables of |add_txf_to_lnf|@>@; {will be declared later}
  @!rasters:integer; {bytes added to font}
  @!i,@!j:integer; {general purpose pointers}
  @t\4@>@<Local procedures of |add_txf_to_lnf|@>@;
begin
  if file_type<>unavailable then
    @<Open the file containing the glyph bitmaps@>;
  @<Find name and magnification of font file@> ;
  case file_type of
  pixel_file:
    @<Prepare to use \.{PXL} file@>;
  packed_file:
    @<Prepare to use \.{PK} file@> ;
  unavailable:
    begin
      font_type[TeX_font]:=missing;
      open_file_name := '[unavailable]'
    end;
  endcases;
  max_ctr := font_ec[TeX_font];
  @<Report name of font file in use@> ;
  if file_type=unavailable then
    write(' unavailable; printing rules for glyphs;');
  print(' with '); write_ln(term_out,' loaded with ');
  @<Determine lowest and highest characters used in the \TeX\ font@>;
  rasters:=4*font_occupancy[TeX_font];
  @<Cycle through the characters of |TeX_font|, downloading as required@> ;
  @<Tidy up after copying rasters from \.{PXL} file@>;
end; {|add_txf_to_lnf|}

@ These variables used to be local to |add_txf_to_lnf|; however, some W{\mc
EB} modules invoked therein are now also used externally, but reference the
variables.  Therefore they are now global.

\yskip\hang|percent| Gives the magnification of the current font, for use in
font loading reports. (An unmagnified font is at 100\%.)

\yskip\hang|name| Holds the ``\TeX-oriented'' font name, for inclusion in
reports on the terminal, whilst the full file specification (from
|open_file_name|) is reported in the log file.

@<Glob...@>=
@!percent:integer;
@!name: file_spec;
@!name_ptr: 0..name_size; {pointer for indexing |name|}

@ As each font is downloaded, we want to report its name and its magnification
(if not \.{\BS magstep 0}) on the terminal, whilst the logfile will receive
the full file specification.  So we can commence by saving the name of the
font file, and the percentage magnification.

@<Find name and magnification of font file@>=
name := '';
for name_ptr:=font_name[TeX_font] to font_name[TeX_font+1]-1 do
  name := name + xchr[names[name_ptr]];  {ASCII representation of font name}
percent:=round((mag*0.1*font_scaled_size[TeX_font])/font_design_size[TeX_font]);

@ With rasters held in an unpacked pixel file, we need to check the file's
validity, and then transfer the directory information into |pxl_dir|.

@<Prepare to use \.{PXL} file@>=
begin
  @<Check validity of PXL file@>;
  @<Read character directory from |pxl_file|@>
end

@ On the other hand, with packed pixel files, we need to read through the
preamble and skip over any specials which precede the first character raster
in the file.

@<Prepare to use \.{PK} file@>=
begin
  @<Read preamble@>; skip_specials;
  if font_check_sum[TeX_font]<>checksum then
      warning('checksum doesn''t match in PK file')
@.checksum doesn't match in PK file@>
end

@ Once we've successfully opened the font file, we can report its name (and any
non-standard magnification).

@<Report name of font file in use@>=
print('Font ',name,' (at ',percent:1,'%) loaded from ',open_file_name);
write(term_out,'Font ',name);
if percent<> 100 then write(' (at ',percent:1,'%)');

@ We now set about creating (a portion of) an LN03 font file from the required
characters of |TeX_font|.  We assume at first that it will be possible to pack
the entire |TeX_font| into a single LN03 font, but may need to revise this
opinion if the font turns out to be longer than 188 characters.

A |while| control construct is used instead of the obvious |for| loop, because
there may be a necessity for premature termination of the loop.

@<Cycle through the characters of |TeX_font|, downloading as required@>=
char_ctr:=font_bc[TeX_font];
while (char_ctr<=max_ctr) do
begin
  @<Download character of font@>;
  incr(char_ctr);
end;
@<Verify that all glyphs were available@>;

@ For each character in the |TeX_font|, we need to see whether it has been
marked as being used, and, if so, whether it belongs to the |ln_font|
currently being created.

@<Download character of font@>=
with glyph_map(TeX_font)(char_ctr) do
if loaded<>unused then
begin
  @<Decide whereabouts to download the character@> ;
  @<Finish off any previous half font on LN03@> ;
  case file_type of
  packed_file:
    @<Copy glyph from packed file, and find the next one@>;
  pixel_file:
    copy_char(char_ctr,to_where);
  unavailable:
    @<Generate dummy directory entry for font that won't be used@>;
  endcases;
end

@ We need to decide whereabouts to put this character glyph in the LN03 font
file being created.  In the case of unpacked pixel files, we can simply use
the mapping determined at an earlier stage.  However, the glyphs in packed
pixel files appear in the same order as the original \MF\ source, so it is
necessary to download them to the LN03 in the order of arrival, otherwise we
may find ourselves jumping between the ``left-hand'' and ``right-hand'' parts
of the code table.

It is at this point that we update |first_txfc| to the next available slot in
the |ln_font|, if necessary, to skip over the non-existent characters between
|left_last| and |right_first|, or from |right_last| to the beginning of an
overflow font (if we're using packed files; unpacked ones, being restricted
to 128 characters, can never require an overflow).

@<Decide whereabouts to download the character@>=
if first_txfc=left_last+1 then
  first_txfc:=right_first
else if (first_txfc=right_last+1) and (file_type=packed_file) then
  @<Prepare for overflow portion, if necessary@>;
to_where:=first_txfc

@ If a font is completely unavailable (perhaps the user has requested it at
a magnification not provided on the current system), we still have to go
ahead and `complete' the font for the LN03, because the font file's headers
already reflect the previously determined mapping.  However, we shall
\\{never} image characters from the font, so we just make the character
locator entries in the font's ``directory'' zero.

@<Generate dummy directory entry for font that won't be used@>=
begin
  @<Create null character locator entry@>;
  loaded:=yes
end

@ Sometimes we find that we cannot complete the bitmap information relating
to a particular character, because the glyph is too large, or missing (or
the font itself is missing).  Since we will already have completed the LN03
font's description relating to number of characters, etc., we must still
provide an entry in the font directory; the LN03 will not attempt to image a
character for which the directory entry is zero, so that's what we put
there!

@<Create null character locator entry@>==
begin
  lnf_long(fnt_char_dir+4*(to_where-dir_base),0);
  decr(num_chars); incr(null_chars)
end


@ Whenever we first meet a character taken from the ``right-hand'' half of the
LN03 font (the set designated as GR, in ISO2022/ANSI X3.64 parlance), we need
to finish off the half font which has been created in the left-hand table.
Similarly, if we meet a character from the ``left-hand'' half (GL) when we had
previously been adding characters to the GR set, it implies that we are
commencing output, to GL, of characters taken from a \TeX\ font which was too
large to be loaded into the 188 characters of the previous |ln_font|.

The font file to date is written out and a new one initialized to hold the GR
character set.  Such successive font files are separated from each other by a
simple `\.,' character in the \.{LN3} file.

@<Finish off any previous half font on LN03@>=
if ((not using_GR) and (to_where>=right_first)) or
   (using_GR and (to_where<=left_last)) then
begin output_LNF;
  write_ln(ln3_file,',');@/
  using_GR:=not using_GR;
  init_LNF
end

@ If glyphs are being taken from a file of packed pixel rasters, we need to
copy the glyph to the LN03 file as the raster is built up from the file.
Having thus disposed of this glyph, we then scan through the file until
finding the \&{next} wanted glyph.

@<Copy glyph from packed file, and find the next one@>=
begin
  repeat
    @<Unpack and write...@>;
    skip_specials
  until (glyph_map(TeX_font)(char_num).loaded<>unused) or
        (flag_byte=pk_post);
  with glyph_map(TeX_font)(char_num) do
  if loaded<>unused then
  begin
    char_code:=first_txfc;
    font_code:=ln_font;
    incr(first_txfc)    {This might later be corrected}
  end;
  if flag_byte=pk_post then char_ctr := 255     {Break out}
end

@ Here are some local variables of |add_txf_to_lnf|, used in the above code:

\yskip\hang|char_ctr| Used to cycle through all possible character codes in
the |TeX_font|.

\yskip\hang|max_ctr| Unpacked pixel files (by their very architecture) can
contain a maximum of 128 glyphs; packed files (in this implementation) may
contain up to 256.  This variable permits more efficient operation by limiting
the values assumed by |char_ctr|.

@<Local variables of |add_txf_to_lnf|@>==
@!char_ctr:integer;
@!max_ctr:integer;


@ After having downloaded the last character of |TeX_font| which belongs in
|ln_font|, we arrange things so that any further characters will be loaded into
|ln_font+1|.   We accordingly want to start at |left_first| (which ought to
be the value held in |txfc_next|), and ensure that the font is written out to
the GR part of |ln_font|, before incrementing to the next font.

This code can only be invoked when working with packed pixel files, because
fonts which run over more than one LN03 font must exceed 188 characters in
length, and unpacked pixel files are limited to 128 characters!

@<Prepare for overflow portion, if necessary@>=
begin
  incr(ln_font);
  last_txfc := final_txfc;    {|first_txfc>right_last|, therefore\dots}
  @<Are we starting or finishing an LN03 font ``file''?@>; {|begin_txf:=false|}
  first_txfc := txfc_next     {This will be our next character}
end


@ The first job |add_txf_to_lnf| has to do is to open the (packed or
unpacked) pixel file; the file directory and font name are passed in to the
function (as parameter |open_file_name|).

Note that this \.{WEB} section is expanded by \.{TANGLE} in the context of
procedure |change_pixel_file| \&{and} of function |add_txf_to_lnf|.  In the
first context, the string variable |open_file_name| referenced herein is the
\&{global} variable of that name, whilst within |add_txf_to_lnf| it is the
formal parameter of that function.

Assuming the file is opened succesfully, the size of the file is taken from
variable |file_len|, which is set up by the \.{user\_action} procedure with
which the file is opened.

The first block of the file is then read (into the associated file variable
|pxl_file|).

@<Open the file containing the glyph bitmaps@>=
begin open(pxl_file,open_file_name,@=readonly@>,@=access_method@>:=@=direct@>,
      @=user_action@>:=file_open,VAX_continue);
file_stat:=status(pxl_file); ask:=file_stat<>0;
if ask then
begin
  case file_stat of
  2: file_message := 'PAS-E-ERRDUROPE';
  3: file_message := 'PAS-E-FILNOTFOU';
  5: file_message := 'PAS-E-ACCMETINC';
  6: file_message := 'PAS-E-RECLENINC';
  7: file_message := 'PAS-E-RECTYPINC';
  8: file_message := 'PAS-E-ORDSPEINC';
  othercases
     file_message := str_int(file_stat);
  endcases;
  abort('Couldn''t open ',open_file_name,'. Status=',file_message);
@:fatal error Couldn't open file}{\quad\.{Couldn't open file} (pxl/pk)@>
end;
reset(pxl_file)
end

@ We apply some simple checks to determine whether the \.{PXL} file passed in
has a valid format.  At the same time, we set up the local variable |pxl_size|
which gives the total length of the \.{PXL} file, in bytes, excluding the
identification longword of 1001 at its end.

@<Check validity...@>=
pxl_size:=file_len;
pxl_block:=0;
move_to_pxl(pxl_size-4);
i:=pxl_ptr+4;           {Point {\bf after} the last block}
repeat
  i:=i-4
until (i<0) or (pxl_long(i)=pxl_id);
pxl_size:=i+VAX_block_length*pxl_block;
if i<0 then abort('PXL file has bad format');
@:fatal error PXL file has bad format}{\quad\.{PXL file has bad format}@>

@ Having found the end of the \.{PXL} file, we are able to extract the
last four longwords.  These give us, starting with the last, the longword
address of the start of the character directory within the file, the ``design
size'' (in scaled points), the ``magnification'' of the font (will be 1500
for fonts designed for 300 pixels/inch devices) and the font checksum.
This latter quantity should match that read from the \.{TFM} file by \TeX.

After using these longwords, we then go to the first byte of the character
directory, and copy it into |pxl_dir|.  Each character's entry consists
of the following fields:
\yskip\hang|num_cols| the number of pixels printed for each row of the
character;
\yskip\hang|num_rows| number of rows of pixels printed;
\yskip\hang|x_off| offset (in pixels) of the left-hand edge of the raster,
relative to the reference point;
\yskip\hang|y_off| similarly for top of raster relative to the baseline;
\yskip\hang|addr| the longword address within the \.{PXL} file of the start
of the rasters;
\yskip\hang|tfm_width| the character's width, in the same format as for
|tfm_file|.

@<Read character directory from |pxl_file|@>=

move_to_pxl(pxl_size-16);
if font_check_sum[TeX_font]<>long_pxl(pxl_ptr) then
  warning('checksum doesn''t match in PXL file');
@.checksum doesn't match in PXL file@>
magnification:=long_pxl(pxl_ptr);
dsize:=long_pxl(pxl_ptr);
dsize:=(dsize/two_to_the_20th)*(magnification/1500.0);@/
move_to_pxl(long_pxl(pxl_ptr)*4);               {Go to start of directory}
for i:=0 to 127 do
  with pxl_dir[i] do
  begin num_cols:=word_pxl(pxl_ptr);
    num_rows:=word_pxl(pxl_ptr);
    x_off:=sign_pxl_word(pxl_ptr);
    y_off:=sign_pxl_word(pxl_ptr);
    addr:=long_pxl(pxl_ptr);
    tfm_width:=long_pxl(pxl_ptr)
  end

@ We need to determine the lowest and highest ordinal character positions of
those characters used in the \TeX\ font being encoded into the LN03 font file;
the variables |first_txfc| and |last_txfc| are used for this purpose.  If there
are no characters used at all in the \TeX\ font some malfunction must have
occurred, because |add_txf_to_lnf| should not have been invoked under those
circumstances.

The character numbers will be those of the LN03 font file, having already been
mapped to these during the mapping phase; therefore, if the lowest character
number used is |= left_first| then we must be starting a new font, so must
initialize the empty font file and indicate that this is a |GL| font
(left-hand half of eight-bit character table).

@<Determine lowest and highest characters used in the \TeX\ font@>=
first_txfc:=-1; last_txfc:=-1;      {For |ln_font|}
txfc_next:=-1; final_txfc:=-1;      {and for |ln_font+1|}
for i:=first_glyph_index(TeX_font) to last_glyph_index(TeX_font) do
  with glyphs[i] do
    @<Note |last_txfc|, and set |first_txfc| for first character@> ;
@<Are we starting or finishing an LN03 font ``file''?@>

@ As we cycle through all the allocated characters, we note each code in turn,
thus determining the highest code used.  We also note the lowest one when we
meet the first code used.  If the |TeX_font| extends into a second |ln_font|,
the corresponding information will be recorded in |final_txfc| and |txfc_next|,
respectively.

@<Note |last_txfc|, and set |first_txfc| for first character@>=
if loaded = wanted then
begin if txf_to_lnf[font_code]=ln_font then
  begin if first_txfc=-1 then first_txfc:=char_code;
    last_txfc:=char_code; font_code:=ln_font
  end
  else  {This must be overflow of |TeX_font| into |ln_font+1|}
  begin if txfc_next=-1 then txfc_next:=char_code;
    final_txfc:=char_code; font_code:=ln_font+1
  end
end

@ If the lowest character code used is |left_first|, then we shall have to
initialize a new LN03 font file.  Similarly, if the last character used is the
last character allocated to this LN03 font, we shall later have to finish off
the latter.  We therefore note these two conditions in |begin_txf| and
|end_txf|, and initialize the font ``file'' if the former condition pertains.

@<Are we starting or finishing an LN03 font ``file''?@>=
if last_txfc<0 then
      abort('Internal error (empty font)');
@:fatal error Internal error empty font}{\qquad\.{(empty font)}@>
begin_txf:=first_txfc=left_first;
end_txf:=last_txfc=last_lnf[ln_font];
if begin_txf then
begin using_GR:=false;
      init_LNF
end

@ Here are some more local variables of |add_txf_to_lnf|, used in the above
code:

\yskip\hang|first_txfc| The lowest character code allocated to a character of
|TeX_font| when the mapping took place.

\yskip\hang|last_txfc| Similarly, the highest code used.

\yskip\hang|txfc_next| The lowest character code for a character of |TeX_font|
allocated to |ln_font+1| when the mapping took place.

\yskip\hang|final_txfc| Similarly, the highest code used by |TeX_font| in
|ln_font+1|

\yskip\hang|begin_txf| A boolean, which is |true| if this |TeX_font| commences
a new font file in the LN03.

\yskip\hang|end_txf| This is |true| if the last character of the present
|TeX_font| is the last character mapped to |ln_font|.

@<Local variables of |add_txf_to_lnf|@>==
@!first_txfc, @!last_txfc : integer;
@!txfc_next, @!final_txfc : integer;
@!begin_txf, @!end_txf : boolean;
@ After all the required character glyphs have been copied from the \.{PXL}
file, we can close it.

If, and only if, the very last character code was that of the highest mapped
into this font, we can output the LN03 font to the \.{LN3} file.

Finally, we report the number of bytes of rasters created in the font file.

@<Tidy up after copying rasters from \.{PXL} file@>=
close(pxl_file,@=disposition@>:=@=save@>,VAX_continue);

if end_txf then output_LNF;

print_ln(rasters:0,' bytes of rasters (',font_occupancy[TeX_font]:1,
                          ' characters)');
write_ln(term_out,rasters:0,' bytes of rasters (',font_occupancy[TeX_font]:1,
                          ' characters)' ,crlf);
add_txf_to_lnf:=rasters {Total bytes of rasters added to file}

@* Procedures to access the font file. The definitions which follow facilitate
access to the individual bytes, words, longwords and strings of the
``|lnf_file|'' by accessing the appropriate entity in the variant record
representation of the file's blocks. The procedures allow us to write bytes,
words and longwords into the memory representation of the |lnf_file|.

@d lnf_bytes(#)==lnf[(#) div VAX_block_length].bytes[(#) mod VAX_block_length]
@d lnf_words(#)==lnf[(#) div VAX_block_length].words[((#) mod VAX_block_length)
div 2]
@d lnf_longs(#)==lnf[(#) div VAX_block_length].longs[((#) mod VAX_block_length)
div 4]
@d lnf_str(#)==lnf[(#) div VAX_block_length].str[((#) mod VAX_block_length) div
4]

@<Local procedures of |add_txf_to_lnf|@>=

procedure lnf_byte(byte_address:integer; byte_val:eight_bits);
begin
  lnf_bytes(byte_address):=byte_val
end;

procedure lnf_word(byte_address:integer; word_val:sixteen_bits);
begin if odd(byte_address) then
        abort('Internal error (word at odd byte ',byte_address:1,')');
@:fatal error Internal error word at odd byte}{\qquad\.{(word at odd byte)}@>
  lnf_words(byte_address):=word_val
end;

procedure lnf_long(byte_address,long_val:integer);
begin if odd(byte_address) then
        abort('Internal error (longword at odd byte ',byte_address:1,')');
@:fatal error Internal error longword at odd byte}{\qquad\.{(longword at odd byte)}@>
  if byte_address mod 4 = 0 then
    lnf_longs(byte_address):=long_val
  else
  begin if long_val<0 then
    begin long_val:=long_val+@'17777777777;     {$2^{31}-1$}
      long_val:=long_val+1;
      lnf_words(byte_address+2):=(long_val div 65536)+32768
    end else
      lnf_words(byte_address+2):=(long_val div 65536);
    lnf_words(byte_address):=long_val mod 65536
  end
end;

@ When we are ``outputting'' to the |lnf_file| from \.{PK} files, it's more
convenient to call the following procedure; in fact, it might prove handy for
use in |copy_char| too!

@<Additional low-level...@>=

procedure put_lnf(@!item : integer);
begin lnf_bytes(base):=item;
   incr(base)
end;
@#
procedure repeat_row;
begin put_lnf(lnf_bytes(row_addr));
   incr(row_addr)
end;

@ For downloading the bitmap of an oversized glyph, we need to ``read'' six
rows of pixels from either the |pxl_file| itself, or from the character's
bitmap which has been formed in |lnf_file| from the |packed_file|.

@<Read rows of pixels into sixel buffer@>=
if pxl_ident=packed_file then
  for j:=1 to ((num_cols+7) div 8) do
  begin
    sixel_line[k,j]:=reverse[lnf_bytes(base)];
    incr(base)
  end else
  begin
    for j:=1 to (num_cols+7) div 8 do
    begin
      sixel_line[k,j]:=pxl_byte(pxl_ptr);
      incr(pxl_ptr); must_get(pxl_ptr)
    end;
    while (pxl_ptr mod 4) <> 0 do
    begin
      incr(pxl_ptr); must_get(pxl_ptr)
    end
  end


@ It could happen that a glyph is imaged that doesn't exist in the font; we
then mark this in the |loaded| field of the |glyph_map| as |missing|; such
glyphs will result in appropriate $(x,y)$ positioning commands being output
to the LN03, by derivation from the font metrics data.

@<Other values of |download_status|@>= ,@!missing

@ After we've read through the (packed or unpacked) pixel file, we should have
managed to download glyphs for all the characters used in the current document.
If the following check finds a character without an associated glyph, it
downloads a dummy, invisible glyph of the appropriate width.

@<Verify that all glyphs were available@>=
for char_num := font_bc[TeX_font] to font_ec[TeX_font] do
  with glyph_map(TeX_font)(char_num) do
    if loaded=wanted then
    begin
      loaded := missing;    {Ensure we don't try to image the glyph}
      term_offset := 20;    {Ensure we get a newline before warning}
      warning('Font ',name,' doesn''t contain character ',char_num:1,crlf);
@:Warning: Font doesn't contain character}{\quad\.{Font doesn't contain...}@>
      @<Decide whereabouts to download the character@>;
      @<Finish off any previous half font on LN03@>;
      tfm_width := width;
      num_cols := pixel_width;
      base := ras_beg + ras_len;
      @<Create null character locator entry@>;
      char_code:=first_txfc; font_code:=ln_font
    end

@*Copy one character's rasters.  For each character taken from an unpacked
(\.{PXL}) file, the function |add_txf_to_lnf| calls this procedure |copy_char|,
which
provides the main action of the function, by converting the \.{PXL} file
rasters to the format required by the \.{LN03}, and storing them away in the
appropriate part of the ``|lnf_file|'' in memory.

The global variable |base| is used as a pointer into the appropriate byte of the
Character Definitions Region of the LN03 font file, and commences at the
location determined from the ``size so far'' as recorded by |ras_beg| and
|ras_len|.

Having verified that the \.{PXL} file contains the character glyph required
(if the character is absent, the address field of the directory entry will not
point to an address within the file), the |move_to_pxl| procedure is called to
make the appropriate bytes of the characters rasters accessible to the
program.

The character parameters section of the character definition is then completed
in the font file; the first four bytes contain the kerning information (null,
because \TeX\ does all the kerning for us, and we don't want the LN03 to upset
it) and 24 bits of flags --- the only bit that we set in this flag field is
the `flag flag' bit.

LN03 pixels (of which there are 300 to an inch) can be converted to DEC
centipoints (or Gutenbergs) by multiplying by 7200/300 = 24.

@d Pxl_to_Gut=24

@<Local procedures of |add_txf_to_lnf|@>=

procedure copy_char(char_num,to_where:integer);
  var @!i,@!j,@!k,@!n,@!len:integer;
begin
  base:=ras_beg+ras_len;        {Where to start saving the parameters/rasters}
  with pxl_dir[char_num] do
  if (addr>0) and (addr<=(pxl_size div 4)-8) then
  {Only process characters which lie within the file!}
  with glyph_map(TeX_font)(char_num) do
  begin
    move_to_pxl(addr*4);
    char_code:=to_where;@/
    @<Process |tfm_width| information@>;
    @<Validate size of character's glyph@>;
    @<Complete remainder of Character Definition parameters@>;
    @<Copy the rasters themselves@>;
    @<Calculate the number of bytes of rasters transferred@>;
    @<Complete the entry in the font character directory@>
  end {|with|}
end;


@ The true character width (in scaled points) is taken from the |tfm_width| field of
the pixel file directory; this value is converted to pixels and stored in the
|ch_widths| array, where it is used by the procedures which set characters on
the page.  The value is also converted to Gutenbergs and stored in the
|fnt_char_width| field for the character raster in the font file.  If the
width differs from that computed from the \.{TFM} file by more than one pixel,
this is reported, and the value derived from the \.{PXL} file substituted in
preference.


@<Process |tfm_width| information@>=
ch_wid:=round((dsize*resolution*tfm_width)/(two_to_the_20th*72.27));
lnf_word(base,@"0000);  {No kerning, lowest byte of flags=0}
lnf_word(base+2,@"8000);        {Highest bit of flags (flag flag) = 1}
lnf_long(base+fnt_char_width,trunc(ch_wid*(7200.0/resolution)));
ch_widths[ln_font,to_where]:=ch_wid;
tfm_wid:=pixel_width;
if abs(ch_wid-tfm_wid) > 1 then
begin warning('Pixel width discrepancy; font ',open_file_name,' char ',char_num:1);
@:Warning: Pixel width discrepancy}{\quad\.{Pixel width discrepancy}@>
  show('---computed width: ',tfm_wid:1,';  actual width: ',ch_wid:1);
end;
pixel_width:=ch_wid

@ The above section of code needs some workspace in which to perform those width
computations.  They may as well be local to |add_txf_to_lnf|.

\yskip\hang|ch_wid| Holds the actual width of the character, in pixels,
derived from the |tfm_width| field of the pixel file's directory.

\yskip\hang|tfm_wid| Holds the character width as calculated from the
|ch_widths| entry which came from the TFM file for this font.

@<Local variables of |add_txf_to_lnf|@>==
@!ch_wid,@!tfm_wid:integer;

@ The \.{PXL} file directory entry contains |x_off| and |y_off| fields, which
define the starting point of the raster relative to the character's reference
point.  These are passed to the font file as the |fnt_left_bearing| and
|fnt_raster_baseline| fields of the raster entry, in Gutenbergs.

The flags longword of the raster is then created, and indicates that the
raster is in portrait orientation (relative to memory, not to the output
device) and is an aligned expanded bit string.  (Unfortunately, although
DEC Std 180 permits run-length encoding of rasters, the LN03 itself does not
support this facility, so rasters have to be downloaded in fully expanded form.)

Invisible fonts (used by Sli\TeX) will have the bounding box info (|num_cols|
and |num_rows|) both equal to zero.  Since the LN03 isn't very happy with such
characters (!), we will later insert a null character locator in the font's
directory; we still go through the motions of generating character
parameters and the (empty) bitmap in the font file, but bytes occupied by
these will be overwritten with the next real bitmap.  Although instructions
to image such a null character are  accepted by the printer, unfortunately
the printer seems to image some other character at random!  Therefore we
mark the glyph as |missing|, so that no attempt is made to image it later.
Oversized glyphs are also not inserted into the font file; so it is only for
genuine printable glyphs that the value of |base| will be advanced to
reserve the space occupied by the character's parameters.

This module is always called in the context |with
glyph_map(TeX_font)(char_num) do|.

@<Complete remainder of Character Definition parameters@>=
lnf_long(base+fnt_left_bearing,int(-Pxl_to_Gut*x_off));
lnf_long(base+fnt_raster_baseline,int(-Pxl_to_Gut*y_off));
lnf_long(base+fnt_char_pars+fnt_char_orient,@"00008100);  {Aligned,bit-expanded}
if num_rows=0 then loaded := missing; {``Invisible'' glyph}
lnf_word(base+fnt_char_pars+fnt_char_cols,num_cols);
lnf_word(base+fnt_char_pars+fnt_char_rows,num_rows);
if loaded=yes then
  base:=base+fnt_char_pars+fnt_char_raster

@ Here are some further values than can be allocated to the |loaded| field
of |glyph_map|: values could be:\par
\item{$\bullet$} |yes| if character's glyph was included in a downloaded LN03
font;
\item{$\bullet$} |no| if the glyph exceeded the LN03's capability for the
area of a downloaded glyph.  In this case a null character locator will have
been inserted in the directory of the downloaded font, and the actual
glyph's bitmap will be sent to the printer each and every time it occurs in
the document.

@<Other values of |download_status|@>= ,@!yes ,@!no

@ The LN03 has a physical (apparently arbitrary) restriction on the maximum size
of a downloadable glyph.  For a glyph to be acceptable, it must satisfy the
inequality $$
  2\times\lceil|num_cols|/2\rceil\times\lceil|num_rows|/8\rceil\le5700$$

The following section prevents such an oversized glyph from being included in
the downloaded font; it is always called in the context |with
glyph_map(TeX_font)(char_num) do|.

@<Validate size of character's glyph@>=
if ((num_rows+7) div 8) * 2 * ((num_cols+1) div 2) > largest_glyph then
  loaded:=no  {Indicate oversized glyph unavailable}
else
  loaded:=yes {Indicate glyph successfully downloaded}


@ After further setting up of values in the font file, we copy the bits of the
rasters from the \.{PXL} file to the LN03 font file; this requires that we
reverse each byte.

As each byte is copied, we get another; the |must_get| procedure will read in
a new block from the \.{PXL} file if necessary.  At the end of each row, the
next is started from the next complete longword of the \.{PXL} file.

Only character glyphs which are capable of being downloaded are entered into
the file; invisible and oversized glyphs don't need to have the bitmap
copied, and they'll eventually end up with a null character locator in the
font file's directory.

This module is always called in the context |with
glyph_map(TeX_font)(char_num) do|.

@<Copy the rasters themselves@>=
if loaded=yes then
for i:=1 to num_rows do
begin
  for j:=1 to (num_cols+7) div 8 do             {Whole bytes required}
  begin lnf_bytes(base):=reverse[pxl_byte(pxl_ptr)];@/
    incr(pxl_ptr); must_get(pxl_ptr); incr(base)
  end;                  {Copied bytes for one row of pixels}
  while (pxl_ptr mod 4)<>0 do
  begin incr(pxl_ptr);  {Row held in 32-bit words in \.{PXL} file}
    must_get(pxl_ptr)
  end
end

@ The |copy_char| procedure requires an array to be set up to ``reverse''
individual bytes' bits, since the \.{PXL} file stores the first pixel of
a row at the most significant bit of the long-word, whilst DEC Std 180
requires the first pixel to be the least significant bit of the first byte.

We therefore create a global array |reverse|, which we initialize with
appropriate patterns to reverse any byte proferred as an index.

@<Globals...@>=
@!reverse: packed array [0..255] of eight_bits;

@ @<Procedures for...@>=
procedure Generate_Reversal;
  var @!index,@!patt,@!cnt,@!rev: eight_bits;
begin
  for index:=0 to 255 do
  begin rev:=0; patt:=index;
    for cnt:=1 to 8 do
    begin rev:=2*rev;
      if odd(patt) then rev:=rev+1;
      patt:=patt div 2
    end;
    reverse[index]:=rev
  end;
end;

@ @<Set initial...@>=
  Generate_Reversal;

@ The number of bytes required for the character glyph in landscape and
portrait modes is computed, and added to the accumulated sum of these for all
characters; the ``mixed'' mode count is increased by the larger of these
quantities.

The value |len| is also computed to be added to the Character Definitions
descriptor held in |ras_beg,ras_len|.  None of these quantities are updated
unless the character glyph has actually been copied into the font file,
i.e., |loaded=yes|.

@<Calculate the number of bytes of rasters transferred@>=
if loaded=yes then
begin
i:=((num_rows+7) div 8)*num_cols; {Bytes required for raster in landscape mode}
lsize:=lsize+i;
j:=((num_cols+7) div 8);                {Bytes/column}
psize:=psize+j*num_rows;        {bytes added to portrait rasters}
msize:=msize+VAX_max(i,j*num_rows); {bytes added to mixed mode rasters}
len:=j*num_rows+fnt_char_pars+fnt_char_raster   {Allow for the parameters}
end else len:=0

@ After transferring the rasters for one character's glyph, we finally
complete the entry in the font file's character directory.  We record the
address of the character's rasters in the appropriate character locator,
update the count of rasters stored, and round up to the next \\{word}
boundary in the font file (note that it is not a requirement of DEC Std 180
that rasters start on a \\{longword} boundary.)

If the glyph was not copied into the font file, because it exceeded the
LN03's maximum size of glyph, or because it was invisible, then a null entry
is made for the character locator, and the next raster, if any, will
overwrite that just stored.

@<Complete the entry in the font character directory@>=
if loaded<>yes then
begin
  lnf_long(fnt_char_dir+4*(to_where-dir_base),0);
  incr(null_chars); decr(num_chars)
end
else begin
  lnf_long(fnt_char_dir+4*(to_where-dir_base),ras_beg+ras_len);
  ras_len:=ras_len+len; rasters:=rasters+len;
  if odd(ras_len) then
  begin
    lnf_byte(base,0);           {Null byte to round raster to word boundary}
    incr(ras_len);
    incr(rasters)
  end
end

@ The following procedure initialises the ``fixed'' area of the \.{LN03} font
file as held in memory.  We start by clearing the whole of the first block of
the file; this is sufficient to ensure that the whole of the file header,
attributes and parameters regions are zeroed, which reduces the number of
individual assigments necessary.

The counts of space occupied by the rasters, and the raster sizes in portrait,
landscape and mixed modes are also initialized.

@<Local procedures of |add_txf_to_lnf|@>=
procedure init_LNF;
var @!i,@!j:integer;
begin
  for i:=0 to 127 do lnf_longs(4*i):=0; {Clear the lnf header}
  ras_len:=0; psize:=0; lsize:=0; msize:=0;@/
  @<Complete the font file's header region@>@;
  @<Complete the font file's attributes region@>@;
  @<Complete the font file's parameters region@>@;
  @<Complete the font file's string pool region@>@;
end;

@ The font file's header contains some identification material, and a number
of descriptors which point to other regions of the font file.

@<Complete the font file's header region@>=
lnf_str(fnt_identifier):='FONT';
lnf_long(fnt_version,1);        {Font File Format is V1.0}
file_ID:='U0000'+@=dec@>(ln_font,2,2)+'002SK00GG0001UZZZZ02F000';
if using_GR then file_ID[21]:='O';
lnf_long(12,VAX_length(file_ID)); lnf_long(16,fnt_file_id);
for j:=1 to VAX_length(file_ID) do lnf_byte(j+fnt_file_id-1,xord[file_ID[j]]);
{For the moment, we set creation date to 11-SEP-1973 14:00:00}
lnf_word(fnt_date,1973); lnf_word(fnt_date+2,9); lnf_word(fnt_date+4,11);
lnf_word(fnt_date+6,14); lnf_word(fnt_date+8,0); lnf_word(fnt_date+10,0);
lnf_long(fnt_attr,fnt_attr_len); lnf_long(fnt_attr+4,fnt_attributes);
lnf_long(fnt_params,fnt_params_len); lnf_long(fnt_params+4,fnt_parameters);
if using_GR then
  num_chars:=last_lnf[ln_font]-right_first+1
else
  if last_lnf[ln_font]<left_last then
    num_chars:=last_lnf[ln_font]-left_first+1
  else
    num_chars:=left_last-left_first+1;
lnf_long(fnt_char_directory,4*num_chars);
lnf_long(fnt_char_directory+4,fnt_char_dir);
lnf_pool_offset:=fnt_char_dir+4*num_chars;
lnf_long(fnt_seg_list,0); { Empty |seg_list| descriptor }
lnf_long(fnt_seg_list+4,0);
lnf_long(fnt_future,0); lnf_long(fnt_future+4,0);
lnf_long(fnt_strings,4*((fnt_string_pool_len+3) div 4));        {Rounded to longword boundary}
lnf_long(fnt_strings+4,lnf_pool_offset);
ras_beg:=lnf_pool_offset+4*((fnt_string_pool_len+3) div 4);
{We fill in address of Kerning Table, but leave it of length zero}
lnf_long(fnt_kerns+4,0);
lnf_long(fnt_chars+4,ras_beg);  {We can't fill in length until finished}
lnf_long(fnt_first,left_first); lnf_long(fnt_last,num_chars-1+left_first);
{No definitions (Yet!!!!) for less than, greater than and error characters}
{No usage of Extension field}
lnf_long(fnt_space," ");        {Space character value}
lnf_long(fnt_organisation,@"A8);
      {Expanded rasters;Identical Orientation; Large Values used}
lnf_long(fnt_no_of_params,fnt_char_pars);
lnf_long(fnt_in_file_count,num_chars);
null_chars:=0; lnf_long(fnt_null_count,null_chars); {Null count maybe revised later}
lnf_long(fnt_char_count,num_chars);
{Alternate blocks remains at zero}
lnf_long(fnt_rasters,num_chars);
{Compressed rasters remains at zero; psize, etc., are filled in at end}
if using_GR then dir_base:=right_first else dir_base:=left_first;
{Establish offset into font's character locators}

@ These used to be local variables of |add_txf_to_lnf|, used in the
previous section.  However, since we now require to use |num_chars| and
|null_chars| to update the font file before it is finally output, they have
to be global.  (Strictly speaking, |file_ID| could remain local, but that
would require much shuffling of the W{\mc EB}!)

\yskip\hang|file_ID| Holds the fixed-length string which identifies any
particular font file to the LN03.

\yskip\hang|num_chars| Gives the number of characters defined in each half of
the LN03 font file.

\yskip\hang|null_chars| Counts any null character locators added to the file
for oversized or missing glyphs.

\yskip\hang|dir_base| Code of the first character loaded in the current half
font; used to derive the correct offset into |font_char_dir| for completing
locators.

@<Global...@>=
@!file_ID:packed array [1..31] of char;
@!num_chars,@!null_chars:integer;
@!dir_base:integer;
@ The font file's attributes region contains mostly descriptors (of strings in
the string pool region) which identify the font, font family, \\{etc.}

@<Complete the font file's attributes region@>=
lnf_long(fnt_attributes+fnt_attr_flags,2);      {Roman}
lnf_long(fnt_attributes+fnt_char_set,fnt_designator_len);
lnf_long(fnt_attributes+fnt_char_set+4,lnf_pool_offset);
lnf_long(fnt_attributes+fnt_type_ID,fnt_type_ID_len);
lnf_long(fnt_attributes+fnt_type_ID+4,lnf_pool_offset+fnt_designator_len);
lnf_long(fnt_attributes+fnt_type_family,fnt_type_family_len);
lnf_long(fnt_attributes+fnt_type_family+4,
              lnf_pool_offset+fnt_designator_len+fnt_type_ID_len);
lnf_long(fnt_attributes+fnt_font_ID,fnt_font_ID_len);
lnf_long(fnt_attributes+fnt_font_ID+4,
      lnf_pool_offset+fnt_designator_len+fnt_type_ID_len+fnt_type_family_len);
{Type category, Font description descriptors empty}
{Following values are purely arbitrary at present; try harder later}
lnf_long(fnt_attributes+fnt_type_size,10);      {``10pt''}
lnf_long(fnt_attributes+fnt_average_width,500); {5.00pt}
lnf_word(fnt_attributes+fnt_resolution+2,24);   {24.0000 centipoints/pixel}
lnf_long(fnt_attributes+fnt_weight,16); {``Normal'' weight}
lnf_long(fnt_attributes+fnt_horiz_prop,16);     {Neither expanded nor compressed}
lnf_word(fnt_attributes+fnt_horiz_ratio,1);     {1:1 relative to a ``normal'' font}
lnf_word(fnt_attributes+fnt_horiz_ratio+2,1);
lnf_word(fnt_attributes+fnt_pixel_aspect,1);    {``Square'' pixels}
lnf_word(fnt_attributes+fnt_pixel_aspect+2,1);
lnf_word(fnt_attributes+fnt_rotation+2,1);      {tan = 0/1 = $0^\circ$}
{Device characteristics = 0}
{Foundry and Designer have empty descriptors}

@ The font file's parameters region contain information relating to all the
characters as a whole.  Since they relate to such features as electronic
underlining, which are not utilized by the \.{LN3} file generated by
DVItoLN03, the values used are purely arbitrary.  (The values are those used
by Flavio Rose's original \.{LN03toPP} (PL/I) program.)

@<Complete the font file's parameters region@>=
{Params flags = 0}
lnf_long(fnt_parameters+fnt_underline_offset,30);
lnf_long(fnt_parameters+fnt_underline_thickness,20);
lnf_long(fnt_parameters+fnt_thru_offset,-60);
lnf_long(fnt_parameters+fnt_thru_thickness,20);
lnf_long(fnt_parameters+fnt_overline_offset,-150);
lnf_long(fnt_parameters+fnt_overline_thickness,20);
lnf_word(fnt_parameters+fnt_slant+2,1); {tan = 0/1 = $0^\circ$}
lnf_word(fnt_parameters+fnt_shadow_offset+2,30);        {$\delta x$ only}
lnf_long(fnt_parameters+fnt_superscript,-90);           {$\delta y$ only}
lnf_long(fnt_parameters+fnt_subscript,40);              {$\delta y$ only}
lnf_long(fnt_parameters+fnt_centreline,60);
lnf_long(fnt_parameters+fnt_min_space,240);     {Note |min>max|!}
lnf_long(fnt_parameters+fnt_max_space,60);
lnf_long(fnt_parameters+fnt_mean_space,100);
lnf_long(fnt_parameters+fnt_em_width,240);
lnf_long(fnt_parameters+fnt_en_width,120);
lnf_long(fnt_parameters+fnt_thin_space,40);
lnf_long(fnt_parameters+fnt_digit_width,120);
lnf_long(fnt_parameters+fnt_topline_offset,-160);
lnf_long(fnt_parameters+fnt_accent,-120);
lnf_long(fnt_parameters+fnt_halfline_offset,-70);
lnf_long(fnt_parameters+fnt_overall_height,240);
lnf_long(fnt_parameters+fnt_height,-180);
lnf_long(fnt_parameters+fnt_depth,60);
lnf_long(fnt_parameters+fnt_H_height,160);
lnf_long(fnt_parameters+fnt_ex_height,120);
lnf_long(fnt_parameters+fnt_white_above,20);
lnf_long(fnt_parameters+fnt_white_below,20);

@ The string pool region of the font file contains the majority of the
character strings which are required to identify the font.

@<Complete the font file's string pool region@>=
lnf_byte(lnf_pool_offset,"0");  {Char set designator}
if using_GR then
  lnf_byte(lnf_pool_offset+1,"<")
else
  lnf_byte(lnf_pool_offset+1,"B");
lnf_byte(lnf_pool_offset+2,9);  {HT character}
for i:=4 to fnt_designator_len do
  lnf_byte(lnf_pool_offset+i-1,"Z");    {|'ZZZZ'|---full char set}
for i:=1 to fnt_type_ID_len do
  lnf_bytes(lnf_pool_offset+i-1+fnt_designator_len):=
              lnf_bytes(i-1+fnt_file_id);       {Copy first 7 of |file_ID|}
for i:=1 to fnt_type_family_len do
  lnf_bytes(lnf_pool_offset+i-1+fnt_designator_len@|+fnt_type_ID_len) :=
              " ";                      {Type family is all spaces}
for i:=1 to fnt_font_ID_len do
  lnf_bytes(lnf_pool_offset+i-1+fnt_designator_len@|+fnt_type_ID_len@|
   +fnt_type_family_len) := lnf_bytes(i-1+fnt_file_id); {Copy first 16 of |file_ID|}

@ The following procedure is invoked whenever an \.{LN03} font file in memory
is ready to be written to the output |ln3_file|.  Firstly we complete those
fields of the font file header region which we were unable to complete before,
storing away the total length (at front \&{and} back) and repeating the
`\.{FONT}' indication at the end of the file.  We also complete the character
definitions region descriptor, and fill in the counts of bytes loaded as
portrait, landscape and mixed mode rasters.

Obviously it is not possible to include the file \\{verbatim} in the output as
a binary image, since the terminal output driver and/or the printer symbiont
would probably eliminate most of those characters which are unprintable.
Therefore the LN03 requires that the binary file be encoded into ``sixel''
format; this is more usually used to transfer graphics information to DEC
printers, one sixel being a printable character (in the range `\.?'$\to$`\.\~')
which encodes the numbers |0..63| representing a column of six pixels.

@<Local procedures of |add_txf_to_lnf|@>=
procedure output_LNF;
  var @!i,@!j,@!k,@!records,@!sixel_ct:integer;
        @!line_out:varying [128] of char;
        @!transform : six_pack;
begin
  lnf_len:=ras_beg+ras_len+8;   {Make room for Trailing identifier}
  lnf_long(fnt_total_size,lnf_len);     {Total size noted into first longword}
  lnf_long(lnf_len-8,lnf_len);  {and into penultimate longword}
  lnf_long(lnf_len-4,lnf_longs(fnt_identifier)); {Copy |'FONT'| from 2nd longword to last}
  lnf_long(fnt_chars,ras_len);  {Length of character definitions}
  lnf_long(fnt_in_file_count,num_chars); {We might have excluded some}
  lnf_long(fnt_null_count,null_chars); {which will be reflected here}
  lnf_long(fnt_rasters,num_chars); {and so we only made this many rasters}
  lnf_long(fnt_portrait,psize);
  lnf_long(fnt_landscape,lsize);
  lnf_long(fnt_mixed,msize);
  @<Sixelize and write@>
end;

@ To convert eight-bit bytes to sixels, which the \.{LN03} requires, we use
(abuse?) \PASCAL's variant record mechanism, to map three bytes into 4 six-bit
characters.

@<Types in...@>==
  @!six_pack = packed record
    case boolean of
      false: (@!bytes : packed array [0..2] of eight_bits);
      true:  (@!sixels: packed array [1..4] of [bit(6)] 0..63)
    end;

@ The three bytes have to be placed in the record such that the earlier bytes
(least significant in the file) are at the left-ward end of the record.  The
resultant six-bit integers are converted to ``printable'' characters by adding
the |ASCII| character `\.{?}', thus giving characters in the range
`\.{?}'$\to$`\.{\~}'.  It is these characters that DEC calls ``sixels'', being
a representation of six pixels in one character.

It is most convenient to process 96 bytes at a time, thus yielding 128
characters records (of sixel characters).  Accordingly, we firstly ensure that
the file is some multiple of 96 bytes long.

@<Sixelize and write@>=
  while (lnf_len mod 96) <> 0 do
  begin lnf_word(lnf_len,0);    { Clear out remainder of ``last record'' }
    lnf_len:=lnf_len+2
  end;          { Can't work in longwords, in case on odd word boundary }
  records:=lnf_len div 96;
  i:=0; j:=0;           {Block/byte in |lnf| array}
  while records > 0 do
  begin
    line_out:='';               {Null string}
    sixel_ct:=0;                {We write 128 sixels (=96 bytes) per record}
    repeat
      k:=2;
      repeat
        transform.bytes[k]:=lnf[i].bytes[j];    {Store bytes from left}
        incr(j); decr(k);
        while j>=512 do
        begin incr(i); j:=j-512 end
      until k<0;                                        {3 bytes at a time}
      for k:=4 downto 1 do
      begin
        line_out:=line_out+chr(transform.sixels[k]+"?");
        incr(sixel_ct)
      end;
    until sixel_ct=128;
    write_ln(ln3_file,line_out);
    decr(records)
  end

@* Skipping pages.
A routine that's much simpler than |do_page| is used to pass over
pages that are not being translated. The |skip_pages| subroutine
is assumed to begin just after the preamble has been read, or just
after a |bop| has been processed. It continues until either finding a
|bop| that matches the desired starting page specifications, or until
running into the postamble.

In this version of \.{DVItoLN03}, it is necessary to ``read'' through all
the pages twice; on the first pass, we gather statistics about the usage
of each character in every font, to minimize the number of rasters
which have to be loaded into the LN03.  This procedure |skip_pages| has
therefore been modified to permit its use during this scan.

In such use, it is first called in the normal fashion, truly to skip pages
until |start_match| indicates that the first page to be output has been
found (or the beginning of the postamble, in the event that the specified
first page does not exist).  This results in |started| becoming |true|.
A further call of |skip_pages|, with |started| asserted, records the use of
each character.  Whilst this ``scanning'' phase is
taking place, the Boolean |scanning| inhibits writing to the \.{TYP} file.

@p procedure skip_pages;
label 9999; {end of this subroutine}
var p:integer; {a parameter}
@!k:0..255; {command code}
@!down_the_drain:integer; {garbage}
@!pages_counted:integer; {counts ``wanted'' pages whilst usage is calculated }
begin
pages_counted:=1; {We've already met the first page requested}
while true do
  begin if eof(dvi_file) then bad_dvi('the file ended prematurely');
@:Bad DVI file the file ended prematurely}{\quad\.{the file ended prematurely}@>
  k:=get_byte;
  p:=first_par(k);
  case k of
  bop: begin @<Pass a |bop| command, setting up the |count| array@>;
    if not started and start_match then
      begin started:=true; goto 9999;
      end;
      if started then
      begin incr(pages_counted); {We've got another page that's wanted}
        if (pages_counted mod 10)=0 then monitor('.'); {show progress}
        if pages_counted>max_pages then {We can skip to the postamble}
        begin old_backpointer:=first_backpointer;
          move_to_byte(post_loc);
          monitor('done.'+crlf); term_offset:=0
        end;
      end;
    end;
  set_rule,put_rule: down_the_drain:=signed_quad;
  fnt_def1,fnt_def1+1,fnt_def1+2,fnt_def1+3: begin define_font(p);
    print_ln(' ');
    end;
  xxx1,xxx1+1,xxx1+2,xxx1+3: while p>0 do
    begin down_the_drain:=get_byte; decr(p);
    end;
  post: begin in_postamble:=true;
    monitor('done.'+crlf); term_offset:=0;
    goto 9999;
    end;
  @<Cases that would set characters on paper@>;
  @<Cases that would change fonts@>;
  othercases do_nothing
  endcases;
  end;
9999:end;

@ If (!) we meet a command that would require a character to be set on the
paper, we note that the relevant character has been used, provided the page
being ``skipped'' is one that is to be printed (\\{i.e.} |started| has been
set).  The |pages_counted| variable is used to inhibit the gathering of such
statistics after the specified number of pages have been ``read''.

We mark both the individual glyph and the font itself as being |wanted|.

@<Cases that would set characters on paper@>=
sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64), @/
four_cases(set1),four_cases(put1): @/
  if started and (pages_counted<=max_pages) then
  begin
    cur_font_glyph(p).loaded:=wanted;
    font_type[cur_font]:=wanted
  end

@ Whenever a font change would have occurred, we determine the |cur_font| so
that the above section, which notes the use of a character, can relate this to
the correct font.

@<Cases that would change fonts@>=
sixty_four_cases(fnt_num_0),four_cases(fnt1): @/
  begin font_num[nf]:=p; cur_font:=0;
    while font_num[cur_font]<>p do incr(cur_font);
    cur_font:=font_map[cur_font];
    cur_base:=glyph_base[cur_font]
  end

@ Global variables called |old_backpointer| and |new_backpointer|
are used to check whether the back pointers are properly set up.
Another one tells whether we have already found the starting page.

@<Glob...@>=
@!old_backpointer:integer; {the previous |bop| command location}
@!new_backpointer:integer; {the current |bop| command location}
@!started:boolean; {has the starting page been found?}

@ @<Set init...@>=
old_backpointer:=-1; started:=false;

@ @<Pass a |bop|...@>=
new_backpointer:=cur_loc-1; incr(page_count);
for k:=0 to 9 do count[k]:=signed_quad;
if signed_quad<>old_backpointer
  then error('backpointer in byte ',cur_loc-4:1,
    ' should be ',old_backpointer:1,'!');
@:Error: backpointer in byte should be}{\quad\.{backpointer...should be...}@>
old_backpointer:=new_backpointer

@* Using the backpointers.
The routines in this section of the program are brought into play only
if |random_reading| is |true|.
First comes a routine that illustrates how to find the postamble quickly.

@<Find the postamble, working back from the end@>=
n:=dvi_length;
if n<53 then bad_dvi('only ',n:1,' bytes long');
@:Bad DVI file only n bytes long}{\quad\.{only $n$ bytes long}@>
m:=n-1;
repeat if m=0 then bad_dvi('no 223s'); {ignore trailing zeroes}
@:Bad DVI file no 223s}{\quad\.{no 223s}@>
  move_to_byte(m); k:=get_byte; decr(m);
until k<>0;
repeat if m=0 then bad_dvi('all 223s');
@:Bad DVI file all 223s}{\quad\.{all 223s}@>
move_to_byte(m); k:=get_byte; decr(m);
until k<>223;
if k<>id_byte then bad_dvi('ID byte is ',k:1);
@:Bad DVI file ID byte is wrong}{\quad\.{ID byte is wrong}@>
move_to_byte(m-3); q:=signed_quad;
if (q<0)or(q>m-33) then bad_dvi('post pointer ',q:1,' at byte ',m-3:1);
@:Bad DVI file post pointer is wrong}{\quad\.{post pointer is wrong}@>
move_to_byte(q); k:=get_byte;
if k<>post then bad_dvi('byte ',q:1,' is not post');
@:Bad DVI file byte n is not post}{\quad\.{byte $n$ is not }\\{post}@>
post_loc:=q; first_backpointer:=signed_quad

@ Note that the last steps of the above code save the locations of the
the |post| byte and the final |bop|.  We had better declare these global
variables, together with another one that we will need shortly.

@<Glob...@>=
@!post_loc:integer; {byte location where the postamble begins}
@!first_backpointer:integer; {the pointer following |post|}
@!start_loc:integer; {byte location of the first page to process}

@ The next little routine shows how the backpointers can be followed
to move through a \.{DVI} file in reverse order. Ordinarily a \.{DVI}-reading
program would do this only if it wants to print the pages backwards or
if it wants to find a specified starting page that is not necessarily the
first page in the file; otherwise it would of course be simpler and faster
just to read the whole file from the beginning.

@<Count the pages and move to the starting page@>=
monitor('Finding starting page...');
@.Finding starting page@>
q:=post_loc; p:=first_backpointer; start_loc:=-1;
if p<0 then in_postamble:=true
else  begin repeat
    {now |q| points to a |post| or |bop| command; |p>=0| is prev pointer}
    if p>q-46 then
      bad_dvi('page link ',p:1,' after byte ',q:1);
@:Bad DVI file page link wrong}{\quad\.{page link wrong...}@>
    q:=p; move_to_byte(q); k:=get_byte;
    if k=bop then incr(page_count)
    else bad_dvi('byte ',q:1,' is not bop');
@:Bad DVI file byte n is not bop}{\quad\.{byte $n$ is not }\\{bop}@>
    for k:=0 to 9 do count[k]:=signed_quad;
    if start_match then start_loc:=q;
    p:=signed_quad;
    @<Note whether we've two \\{recto} pages in succession@>;
  until p<0;
  if start_loc<0 then abort('starting page number could not be found!');
@:fatal error starting page number}{\quad\.{starting page number...}@>
  move_to_byte(start_loc+1); old_backpointer:=start_loc;
  for k:=0 to 9 do count[k]:=signed_quad;
  p:=signed_quad; started:=true;
  end;
if page_count<>total_pages then
  warning('there are really ',page_count:1,' pages, not ',total_pages:1,'!');
@:Warning: there are really n pages}{\quad\.{there are really $n$ pages}@>
monitor('found.'+crlf); term_offset:=0

@ One of the reasons for using the above routine (which seems rather
wasteful if the user wants to print pages starting from the beginning of the
\.{DVI} file) is so that we can determine whether any particular \\{recto}
page is followed by another, without an intervening \\{verso} page.  This
information can then be used at printing time to change the printing mode of
the DEClaser~2200 (LN06), which supports duplex printing, such that the
first of these pages be printed in simplex mode.  Doing this speeds up the
printing cycle compared with merely outputting a blank \\{verso} page,
since the mechanical operation of turning over the paper is thereby avoided.

Therefore we construct an array of pointers into the \.{DVI} file where an
entry indicates that the page is followed by a blank \\{verso} sheet.  Here
are the necessary data structures:

@<Glob...@>=
@!blank_follows:array[1..max_blank_pages] of integer;
@!ptr_blanks:0..max_blank_pages;
@!next_is_recto:boolean;

@ Naturally we initialize this such that a final \\{recto} page will have
its blank \\{verso} side treated by the same mechanism.

@<Set init...@>=
next_is_recto:=true; ptr_blanks:=0;

@ As we work our way backwards through the file, we have the opportunity to
recognize that a \\{recto} page is followed directly by another of the same,
without an intervening \\{verso} page.  This code relies upon \TeX\ having
used its \.{\\count0} for the page number, which is the usual convention,
even when other counters are used to provide additional information in page
numbering (such as the chapter number).  When we later process the
information in the |blank_follows| array, we do so in reverse order, which
therefore corresponds with the processing of the file in the forward
direction.

@<Note whether we've two \\{recto} pages in succession@>=
if odd_page and next_is_recto then
  if ptr_blanks=max_blank_pages then
    capacity_exceeded('too many blank verso pages')
@:capacity exceeded too many blank verso}{\quad\.{too many blank }\\{verso}\.{ pages}@>
  else
  begin
    incr(ptr_blanks); blank_follows[ptr_blanks]:=cur_loc
  end;
next_is_recto:=odd_page

@* Reading the postamble.
Now imagine that we are reading the \.{DVI} file and positioned just
four bytes after the |post| command. That, in fact, is the situation,
when the following part of \.{DVItoLN03} is called upon to read, translate,
and check the rest of the postamble.

@p procedure read_postamble;
var k:integer; {loop index}
@!p,@!q,@!m:integer; {general purpose registers}
begin post_loc:=cur_loc-5;
print_ln('Postamble starts at byte ',post_loc:1,'.');
@.Postamble starts at byte n@>
if signed_quad<>numerator then
  warning('numerator doesn''t match the preamble!');
@:warning numerator doesn't match}{\quad\.{numerator doesn't match...}@>
if signed_quad<>denominator then
  warning('denominator doesn''t match the preamble!');
@:warning denominator doesn't match}{\quad\.{denominator doesn't match...}@>
if signed_quad<>mag then if new_mag=0 then
  warning('magnification doesn''t match the preamble!');
@:warning magnification doesn't match}{\quad\.{magnification doesn't match...}@>
max_v:=signed_quad; max_h:=signed_quad;@/
max_v_saved:=max_v; max_h_saved:=max_h;@/
print('maxv=',max_v:1,', maxh=',max_h:1);@/
max_s:=get_two_bytes; total_pages:=get_two_bytes;@/
print_ln(', maxstackdepth=',max_s:1,', totalpages=',total_pages:1);
@<Process the font definitions of the postamble@>;
@<Make sure that the end of the file is well-formed@>;
end;

@ When we get to the present code, the |post_post| command has
just been read.

@<Make sure that the end of the file is well-formed@>=
q:=signed_quad;
if q<>post_loc then
  error('bad postamble pointer in byte ',cur_loc-4:1,'!');
@:Error: bad postamble pointer}{\quad\.{bad postamble pointer}@>
m:=get_byte;
if m<>id_byte then error('identification in byte ',cur_loc-1:1,
@:Error: identification in byte p should be n}{\quad\.{identification...should be $n$}@>
    ' should be ',id_byte:1,'!');
k:=cur_loc; m:=223;
while (m=223)and not eof(dvi_file) do m:=get_byte;
if not eof(dvi_file) and (m<>0) then bad_dvi('signature in byte ',cur_loc-1:1,
@:Bad DVI file signature...should be}{\quad\.{signature...should be...}@>
    ' should be 223')
else if cur_loc<k+4 then
  warning('not enough signature bytes at end of file (',
@:Warning: not enough signature bytes}{\quad\.{not enough signature bytes...}@>
    cur_loc-k:1,')');

@ @<Process the font definitions...@>=
repeat k:=get_byte;
if (k>=fnt_def1)and(k<fnt_def1+4) then
  begin p:=first_par(k);
    define_font(p);
    if not substitution_pass then print_ln(' ');
    k:=nop;
  end;
until k<>nop;
if k<>post_post then
  error('byte ',cur_loc-1:1,' is not postpost!')
@:Error: byte n is not postpost}{\quad\.{byte $n$ is not }\\{postpost}@>

@* The main program.
Now we are ready to put it all together. This is where \.{DVItoLN03} starts,
and where it ends.  These macros define various possible exit values for the
program, through the VAX/VMS system service \.{\$exit}.
@^system dependencies@>

@d VAX_exit==@=$exit@>
@d VAX_ss_normal==@= sts$k_success @>
@d VAX_ss_warning==@= sts$k_warning + sts$m_inhib_msg @>
@d VAX_ss_error==@= sts$k_error + sts$m_inhib_msg @>
@d VAX_ss_fatal==@= sts$k_severe + sts$m_inhib_msg @>

@p begin initialize; {get all variables initialized}
  dialog; {set up all the options}
  write_ln(term_out,'(',actual_file_spec,crlf);
  print_ln('Reading from file ',actual_file_spec);
  @<Process the preamble@>;
  @<Find the postamble, working back from the end@>;
  in_postamble:=true; read_postamble; in_postamble:=false;
  @<Count the pages and move to the starting page@>;
  if not in_postamble then @<Gather font usage statistics@>;
  if not in_postamble then @<Translate up to |max_pages| pages@>;
  close(ln3_file,@=disposition:=save@>,VAX_continue);
  monitor(')');
final_end:
  ask := (history > spotless) or odd(VAX_cli_present('LOG'));
  if VAX_cli_present('LOG') = VAX_cli_negated then ask := false;
  if ask then
    close(type_file,@=disposition:=save@>,VAX_continue)
  else
    close(type_file,@=disposition:=delete@>,VAX_continue);
  case history of       { Issue an appropriate VAX exit status }
  spotless: VAX_exit(VAX_ss_normal);    { Everything OK! }
  warning_given: VAX_exit(VAX_ss_warning);
  error_given: VAX_exit(VAX_ss_error);
  fatal_error: VAX_exit(VAX_ss_fatal)
  end;
end.

@ The main program needs a few global variables in order to do its work.

@<Glob...@>=
@!k,@!m,@!n,@!p,@!q:integer; {general purpose registers}

@ A \.{DVI}-reading program that reads the postamble first need not look at the
preamble; but \.{DVItoLN03} looks at the preamble in order to do error
checking, and to display the introductory comment.

@<Process the preamble@>=
open_dvi_file;
p:=get_byte; {fetch the first byte}
if p<>pre then bad_dvi('First byte isn''t start of preamble!');
@:Bad DVI file First byte isn\'t}{\quad\.{First byte isn't...}@>
p:=get_byte; {fetch the identification byte}
if p<>id_byte then
  error('identification in byte 1 should be ',id_byte:1,'!');
@:Error: identification in byte p should be n}{\quad\.{identification...should be $n$}@>
@<Compute the conversion factor@>;
p:=get_byte; {fetch the length of the introductory comment}
print('''');
while p>0 do
  begin decr(p); print(xchr[get_byte]);
  end;
print_ln('''')

@ The conversion factor |conv| is figured as follows: There are exactly
|n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch,
and |resolution| pixels per inch. Then we have to adjust this
by the stated amount of magnification.

@<Compute the conversion factor@>=
numerator:=signed_quad; denominator:=signed_quad;
if numerator<=0 then bad_dvi('numerator is ',numerator:1);
@:Bad DVI file numerator is wrong}{\quad\.{numerator is wrong}@>
if denominator<=0 then bad_dvi('denominator is ',denominator:1);
@:Bad DVI file denominator is wrong}{\quad\.{denominator is wrong}@>
print_ln('numerator/denominator=',numerator:1,'/',denominator:1);
conv:=(numerator/254000.0)*(resolution/denominator);
mag:=signed_quad;
if new_mag>0 then mag:=new_mag
else if mag<=0 then bad_dvi('magnification is ',mag:1);
@:Bad DVI file magnification is wrong}{\quad\.{magnification is wrong}@>
true_conv:=conv; conv:=true_conv*(mag/1000.0);
print_ln('magnification=',mag:1,'; ',conv:16:8,' pixels per DVI unit')

@ The code shown here uses a convention that has proved to be useful: If the
starting page was specified as, e.g., `\.{1.*.-5}', then all page numbers in
the file are displayed by showing the values of counts 0, 1, and~2, separated
by dots. These numbers are displayed on the terminal when DVItoLN03 is working
on that page.

The parameter |-1| in the call of |do_page| indicates to the latter that
this is \&{not} a recursive call of the function, and hence that it should
initialize its data structures for a fresh page.

@<Translate up to...@>=
begin while max_pages>0 do
  begin decr(max_pages);
  print_ln(' '); print(cur_loc-45:1,': beginning of page ');
  text_out:='[';
  last_counter:=0;
  for k:=0 to 9 do if count[k]<>0 then last_counter:=k;
  for k:=0 to last_counter do
    begin
      if (k=0) or (count[k]<>0) then
      begin
        print(count[k]:1); text_out:=text_out+str_int(count[k])
      end;
      if k<last_counter then
      begin print('.'); text_out:=text_out+'.' end
      else print_ln(' ');
    end;
  monitor(text_out);    {Let user know which page has started}
  cur_loc_after_bop:=cur_loc;  {For checking on blank \\{verso} pages}
  if not do_page(-1) then bad_dvi('page ended unexpectedly');
@:Bad DVI file page ended unexpectedly}{\quad\.{page ended unexpectedly}@>
  incr(pages_printed);
  repeat k:=get_byte;
  if (k>=fnt_def1)and(k<fnt_def1+4) then
    begin p:=first_par(k); define_font(p); k:=nop;
    end;
  until k<>nop;
  write_ln(term_out,']'); incr(term_offset); {Let user know page has finished}
  if k=post then
    begin in_postamble:=true; goto done;
    end;
  if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop');
@:Bad DVI file byte n is not bop}{\quad\.{byte $n$ is not }\\{bop}@>
  @<Pass a |bop|...@>;
  end;
done: @<Conditionally eject last page@>
end

@  A string variable |text_out| is provided to hold the
characters of a page number.  We also use |last_counter| to indicate the
last significant item in the |count| array when displaying the page number.

@<Globals...@>=
  @!text_out : file_spec;
  @!last_counter : 0..9;

@ No output for the terminal occurs before the pages are processed except
for error and font load messages, all of which end with a newline.  Therefore,
we are quite safe in resetting |term_offset| during the initialisation phase.

@<Set initial...@>=
  term_offset:=0;

@* System-dependent changes.
Here are the remaining changes to the program
that are necessary to make \.{DVItoLN03} work on Vax/VMS.
@^system dependencies@>

We define here various variables required by the VAX.

\yskip\hang |history| is used to record whether any warning or error
messages (including fatal errors) have been reported.

\yskip\hang |type_file| and |ln3_file| are |file| variables used for writing
the log and
output files, respectively.  Advantage is taken of VAX-\PASCAL's |varying|
arrays to hold variable length strings: |file_name| is used generally for
names of files, whilst |def_file_name| initially holds the specification of
the \.{.dvi} file being processed, from which the name is later extracted for
use in forming the names of generated files.

The remaining variables are used during the parsing of the file
specification.

@<Glob...@>==
@!history: exit_status;
@!type_file: text;
@!ln3_file: text;
@!file_name,@!actual_file_spec,@!log_file_name,
   @!output_file_name:file_spec;
@!file_message:file_spec;
@!cmd_j,@!cmd_k:integer;
@!ask : boolean;

@ Here is the enumeration type used for |history|.

@<Types...@>==
@!exit_status = (@!spotless,@!warning_given,@!error_given,@!fatal_error);
@ We now do all the VMS specific things to open the files required, etc. The
\.{DVIfile} is opened with a fixed block length, and with \.{direct} access
method, to permit |random_reading|.  A |user_action| procedure is called, to
perform the actual opening of the file; this gives us access to the various
VAX data structures (|FAB|,|RAB| and |XAB|) and thus we are able to determine
the actual length of the file.

The |output| file (directed to the terminal) is opened without
|carriage_control|, in order that |write_ln| may be used to ensure that
each partial line of output is displayed on the screen.

@<Preset init...@>=
open(output,'SYS$OUTPUT',@=carriage_control:=none@>,VAX_continue);
history := spotless;  { We haven't had any errors (yet!) }
@<Get name of \.{DVI} file from command-interpreter, and open it@>@;
@<Extract just the file name from the specification of the \.{DVI} file@>@;
@<Extract the |log_file_name| from the \.{/LOG} qualifier@>@;
@<Create and open the \.{TYP} log file@>@;
@<Extract the \TeX\ Font Metric directory from the qualifiers@>;
@<Extract the font directories from the qualifiers@>@;
@<Extract the virtual font directory from the qualifiers@>@;
@<Extract the |output_file_name| from the \.{/OUTPUT} qualifier@>@;
@<Create and open the \.{LN3} output file@>@;

@ When we parse the qualifiers pertaining to the physical font directories,
we use the variable length strings |tex_pxl_font| and |tex_pk_font|  to hold
the directory specifications derived from the command line qualifiers
\.{/PXL\_FONT\_DIRECTORY} and \.{/PK\_FONT\_DIRECTORY}, respectively.  Iff,
after applying logical name translation (if necessary),  a file directory
specification is found to end in the sequence `\.{.]}', then a rooted file
directory structure will be assumed; this is recorded in the variables
|pk_rooted| and |pxl_rooted|.

There is no such complication for the \.{/TFM\_DIRECTORY} qualifier; its
value is merely copied to |tfm_directory|.

@<Glob...@>=
@!tex_pxl_font,@!tex_pk_font,@!tfm_directory:file_spec;
@!pxl_rooted,@!pk_rooted:boolean;

@ Originally, this program had the location of the \TeX\ Font Metrics files
(\.{.TFM}) `hard-wired' into it, just as the VMS implementation of \TeX\
itself did, through the logical name \.{TEX\$FONTS}.  \TeX\ has since been
modified so that such information is communicated to the program through a
command-line qualifier, and this program too now makes use of the same
mechanism.

This permits two things:

\yskip\hang$\bullet$ The site manager can chose whether to use logical names
of the form \.{TEX\UL FONTS} or \.{TEX\$FONTS} ({\sl Digital\/} recommends that
customers should \&{not} define logical names containg the `\.\$' character,
since the logicals which they themselves define \&{always} contain this
character, and may at some future date conflict with customer-defined
logicals).

\yskip\hang$\bullet$ Individual users may define their own logical names
that provide a search-list for a number of directories; this can prove
useful whilst testing a new font that should not be installed for general
access.

@<Extract the \TeX\ Font Metric directory from the qualifiers@>=
if odd(VAX_cli_present('TFM_DIRECTORY')) then
  k:=VAX_cli_get_value('TFM_DIRECTORY',tfm_directory)
else
  tfm_directory:=''

@ At this point we ``read'' the font directory specifications passed as the
values of the qualifiers \.{/PXL\_FONT\_DIRECTORY} and
\.{/PK\_FONT\_DIRECTORY}.  At least one of these must be provided with a
value.  The specifications are transferred to the variables |tex_pxl_font| and
|tex_pk_font| respectively, and logical name translation is applied.  If the
resultant string doesn't end with a `\.]', an error is signalled.  If the
character \&{preceding} this bracket is `\..', the final `\.]' is discarded.

@<Extract the font directories from the qualifiers@>==
tex_pk_font:=''; tex_pxl_font:='';
ask:=odd(VAX_cli_present('PK_FONT_DIRECTORY'));
if ask then
begin
  VAX_cli_get_value('PK_FONT_DIRECTORY',tex_pk_font);
  Translate(tex_pk_font,file_name);
  if file_name[file_name.length] <> ']' then
    abort('Bad /PK_FONT_DIRECTORY qualifier; doesn''t end with a `]''');
@:fatal error Bad pk_font_directory}{\quad\.{Bad /PK_FONT_DIRECTORY qualifier}@>
  pk_rooted := file_name[file_name.length-1] = '.';
end;
if odd(VAX_cli_present('PXL_FONT_DIRECTORY')) then
begin
  VAX_cli_get_value('PXL_FONT_DIRECTORY',tex_pxl_font);
  Translate(tex_pxl_font,file_name);
  if file_name[file_name.length] <> ']' then
    abort('Bad /PXL_FONT_DIRECTORY qualifier; doesn''t end with a `]''');
@:fatal error Bad pxl_font_directory}{\quad\.{Bad /PXL_FONT_DIRECTORY qualifier}@>
  pxl_rooted := file_name[file_name.length-1] = '.';
  ask:=true;
end;
if not ask then abort('Where are the font directories? (/PK_FONT_DIRECTORY, etc)');
@:fatal error Where are the font directories}{\quad\.{Where are the font directories?}@>

@ Similarly, we determine the directory in which virtual font metrics are to
be found; if the qualifier \.{/VIRTUAL\UL DIRECTORY} is negated, then
virtual font support is explicitly suppressed.

@<Extract the virtual font directory from the qualifiers@>==
tex_virtual:='';
i := VAX_cli_present('VIRTUAL_DIRECTORY');
no_virt_support := i = VAX_cli_negated;
if odd(i) then
  VAX_cli_get_value('VIRTUAL_DIRECTORY',tex_virtual);

@ The variable length string |tex_virtual| receives this definition.  If
\.{/NOVIRTUAL\UL DIRECTORY} has been specified, this is recorded in
|no_virt_support|.

@<Glob...@>=
@!tex_virtual:file_spec;
@!no_virt_support:boolean;

@ We now ask the Vax/VMS command-line interpreter to give us the file
specification furnished by the user to the command which invokes DVItoLN03.
The file is opened \.{readonly} to preclude any possibility of corrupting or
deleting it, and the \.{user\_action} paramter of the VAX-\PASCAL\ |open|
procedure is used, so that we can determine the full file specification and
length of the file, which is also opened for random (\.{direct}) access.

We can't use |abort| for the fatal error reports, because the log file
hasn't yet been opened, so we define this instead:

@d bomb_out(#)==begin write_ln(output,crlf,'Fatal error: ',#,crlf); jump_out end

@<Get name of \.{DVI} file from command-interpreter, and open it@>=
def_file_name:='';
ask:=odd(VAX_cli_get_value('FILESPEC',def_file_name));
if not ask then bomb_out('No file name provided');
@.No file name provided@>
@:fatal error no file name provided}{\quad\.{No file name provided}@>
open(dvi_file,def_file_name,@=readonly@>,@=access_method:=direct@>,
        @=user_action@>:=file_open,@=default@>:='.DVI;0',VAX_continue);
ask:=status(dvi_file)<>0; dvi_size:=file_len;
if ask then bomb_out('Couldn''t open ',def_file_name);
@:fatal error couldn\'t open DVI}{\quad\.{Couldn't open .DVI file}@>
actual_file_spec:=def_file_name;

@ The user has the option to specify an explicit file name on the \.{/LOG}
qualifier.  If none is specified, or the qualifier is absent, the log file
will be created in the current directory, with the same name as the input
file and a file extension of `\.{.TYP}'.  These are also the defaults used
if the relevant portion of the file specification is not given with the
\.{/LOG} qualifier.

@<Extract the |log_file_name| from the \.{/LOG} qualifier@>=
log_file_name:='';  {Null name specified in case defaults required}
if odd(VAX_cli_present('LOG')) then
  VAX_cli_get_value('LOG',log_file_name);

@ After we've successfully opened the \.{DVI} file (and are thus assured that
it exists), we strip off any node, device or directory specification from the
front of the string, and also remove any trailing file type or version number
specification.  This makes use of the VAX-\PASCAL\ |substr| function, but
could readily be recoded into standard \PASCAL.

@<Extract just the file name from the specification of the \.{DVI} file@>=
cmd_j:=1;  {Strip off any leading node, device or directory name}
for cmd_k:=1 to def_file_name.length do
  if (def_file_name[cmd_k]=']')
    or (def_file_name[cmd_k]=':')
      or (def_file_name[cmd_k]='>')
        then cmd_j:=cmd_k+1;
if cmd_j<=def_file_name.length then
  def_file_name:=substr(def_file_name,cmd_j,def_file_name.length-cmd_j+1);@/
cmd_j:=0; {and then strip off any trailing file type or version indication}
for cmd_k:=1 to def_file_name.length do
  if (cmd_j=0) and @/
    ((def_file_name[cmd_k]='.') or (def_file_name[cmd_k]=';')) then
      cmd_j:=cmd_k;
if cmd_j=0 then cmd_j:=def_file_name.length+1;
def_file_name:=substr(def_file_name,1,cmd_j-1);

@ We now |open| the \.{TYP} file in the current directory, to act as a log
file for this run of DVItoLN03.  The file is opened with |disposition:=save|
so that it will still `exist' even in the event of a crash; this means that the
user can interrupt the program (with Ctrl-Y) and be assured that the only
file that will be retained is the log.

@<Create and open the \.{TYP} log file@>=
file_name:=def_file_name+'.TYP';
open(type_file,log_file_name,@=new@>,@=32767@>,@=disposition:=save@>,
        @=default@>:=file_name,VAX_continue);
ask:=status(type_file)>0;
if ask then bomb_out('Couldn''t create ',file_name);
@:fatal error couldn\'t create TYP}{\quad\.{Couldn't create .TYP file}@>
rewrite(type_file);

@ A particularly useful qualifier for those with limited disk quota is
\.{/OUTPUT}, which permits a user to specify a different output file
specification from the default, which is to open the output file in the
current directory with a name taken from the input (\.{.DVI}) file, and file
extension `\.{.LN3}'.  Using this qualifier permits a user to divert the
output to a scratch disk.

@<Extract the |output_file_name| from the \.{/OUTPUT} qualifier@>=
output_file_name := ''; {Prepare to use the default name}
if odd(VAX_cli_present('OUTPUT')) then
  VAX_cli_get_value('OUTPUT',output_file_name);

@ Finally, we create and |open| the \.{LN3} file.  Again this is opened with
|disposition:=delete|.

@<Create and open the \.{LN3} output file@>=
file_name := def_file_name+'.LN3';
open(ln3_file,output_file_name,@=new@>,@=256@>,@=disposition@>:=@=delete@>,
                @=carriage_control@>:=@=none@>,
                @=default@>:=file_name,VAX_continue);
ask:=status(ln3_file)>0;
if ask then begin
  abort('Couldn''t create ',file_name);
@:fatal error couldn\'t create LN3 file}{\quad\.{Couldn't create .LN3 file}@>
  goto final_end
end;
rewrite(ln3_file);

@ Whenever a binary file is opened, the optional |user_action| parameter is
provided to the call of the VAX-\PASCAL\ |open| procedure; this therefore
invokes the procedure |file_open| (in the next section) which is therefore
able to determine the full specification of the file opened, and also its
exact length; the former is placed into |def_file_name|, and the latter in
|file_len|.

The following definitions permit more \PASCAL-like references to the VAX/RMS
data structures within |file_open|

@d VAX_FAB_type == @=fab$type@>
@d VAX_RAB_type == @=rab$type@>
@d VAX_NAM_type == @=nam$type@>
@d VAX_XAB_type == @=xab$type@>
@d VAX_RMS_open == @=$OPEN@>
@d VAX_RMS_connect == @=$CONNECT@>
@d VAX_fab_to_nam == @=fab$l_nam@>      {Pointer in FAB to NAM block}
@d VAX_nam_length == @=nam$b_rsl@>      {Length of resultant file name}
@d VAX_nam_address == @=nam$l_rsa@>     {and its whereabouts}
@d VAX_fab_to_xab == @=fab$l_xab@>      {Pointer in FAB to first XAB}
@d VAX_xab_code == @=xab$b_cod@>        {Byte in XAB which identifies its type}
@d VAX_xab_fhc == @=xab$c_fhc@>         {XAB contains file header characteristics}
@d VAX_xab_next == @=xab$l_nxt@>        {Pointer to another XAB}
@d VAX_end_block == @=xab$l_ebk@>       {Number of file block containing EOF}
@d VAX_first_free == @=xab$w_ffb@>      {First free byte in last block}

@<Glob...@>=
@!def_file_name : file_spec;
@!file_len : integer;

@ Here is the procedure |file_open|, which determines the actual length of
the file being opened.  It also extracts the full file specification of the
\.{DVI} which it opens, and reports this back in |def_file_name|.

Vax/VMS uses RMS (Record Management Services) to handle file operations, and
this utilizes various structures, in particular, the FAB (File Access Block)
and the RAB (Record Access Block).  There are also a number of different
XABs (eXtension Access Block), one of which contains the required
information relating to the size of the file.

A |user_action| procedure such as this is invoked by the VAX-\PASCAL\ |open|
procedure after it has set up the FAB and RAB structures.  The user is
therefore able at that time to change these structures to alter the processing
algorithms used by RMS.  The file has \&{not}, however, been opened at that
time; it is necessary to call the RMS \.{\$OPEN} service.  Once the RAB has
been completed to the user's satisfaction, the \.{\$CONNECT} service is called
to associate the file variable with the records of the file.

Due to the nonsensical manner in which the |FAB|, etc., are declared in
\.{SYS\$LIBRARY:STARLET.PAS}, wherein the pointers to |XAB|s are declared
to be of type |unsigned|, it is necessary for us to use type casts
for assignments and usage as a pointer!

@<Procedures for init...@>=
function file_open(var @!fab:VAX_FAB_type; var @!rab:VAX_RAB_type;
                var @!f_spec:byte_file): integer;
type @!xab_ptr = ^VAX_XAB_type;
     @!nam_ptr = ^VAX_NAM_type;
     @!char_ptr = ^char;
var @!status:integer;@/
    @!nam : nam_ptr;
    @!xab : xab_ptr;
    @!actual_length : integer;
    @!next : char_ptr;
    @!i : integer;
begin file_len:=-1;     {In case we can't determine actual file size}
  status:=VAX_RMS_open(fab);    {Fill in the FAB and RAB}
  if odd(status) then
  begin status:=VAX_RMS_connect(rab);   {Link the RAB to RMS}
    if odd(status) then
    begin
      nam:=fab.VAX_fab_to_nam::nam_ptr;
      if nam<>nil then
      begin
        actual_length := nam^.VAX_nam_length;
        def_file_name := '';    {Reset to empty}
        next:=nam^.VAX_nam_address::char_ptr;
        for i:=1 to actual_length do
        begin
          def_file_name := def_file_name + next^;
          next:=(next::integer + 1)::char_ptr;
        end;
      end;
      xab:=fab.VAX_fab_to_xab::xab_ptr;
      {Find the File Header Characteristics}
      while (xab<>nil) and (xab^.VAX_xab_code<>VAX_xab_fhc) do
        xab:=xab^.VAX_xab_next::xab_ptr;
      if xab<>nil then
        with xab^ do
          file_len:=int((VAX_end_block-1)*VAX_block_length+VAX_first_free);
    end;
  end;
  file_open:=status;    {Return success or failure}
end; {|file_open|}

@ This function provides us with logical name translation.  It utilizes the
\.{SYS\$TRNLNM} system service in preference to the obsolete \.{SYS\$TRNLOG}.

On entry, any terminal `\.:' on the string |log_name| is discarded before
attempting logical name translation.  If the latter succeeds, the translation
is written back to |equivalence_string| and the function returns |true|; if
translation fails, the original |log_name| is returned in |equivalence_string| and
|false| returned.

It is necessary to provide the local variable |copy_name| during the
translation process, because VAX-\PASCAL\ does not permit the address of a
|var| parameter to be taken, so we cannot use |equivalence_string| to receive
the successive translations, and yet the address of the string must be passed
to the system service as the buffer to receive the translated logical name.
Similarly, we cannot use |log_name|, since that will be a local copy, so that
|string_size| will be that of the original logical name.

The next macro is used to hide the VAX-\PASCAL\ conformant schema from W{\mc
EAVE}, and thus permit more meaningful formatting.

@d VAX_conformant_schema(#)==VAX_volatile varying [#] of char
@#
@d VAX_trnlnm == @=$TRNLNM@>
@d VAX_lnm_case_blind==@= lnm$m_case_blind@>

@<Procedures for initialization@>==
function Translate(log_name : VAX_conformant_schema(string_size);
   var equivalence_string : VAX_conformant_schema(buf_size) ) : boolean;
  var @!i, @!status : integer;
      @!copy_name : VAX_volatile file_spec;
      @!return_length : VAX_volatile integer;
      @!attributes : unsigned;
      @!item_list : VAX_volatile array [0..1] of VMS_item_list;
begin
  equivalence_string := log_name;  {In case translation fails}
  copy_name := log_name;
  if copy_name[copy_name.length] = ':' then
    decr(copy_name.length);
  attributes := VAX_lnm_case_blind;
  return_length := 0;
  with item_list[0] do
  begin
    buffer_length := buf_size;
    item_code := @=LNM$_STRING@>;
    buffer_addr := @=iaddress@>(copy_name.body);
    ret_len_addr := @=iaddress@>(return_length);
  end;
  item_list[1].next_item := 0;
  status := VAX_trnlnm(attributes,'LNM$DCL_LOGICAL',copy_name,,item_list);
  if not odd(status) then
    Translate := false
  else
  begin
    copy_name.length := return_length;
    return_length := 0;
    while odd(VAX_trnlnm(attributes,'LNM$DCL_LOGICAL',copy_name,,item_list)) do
    begin
      copy_name.length := return_length;
      return_length := 0;
    end;
    Translate := true;
    equivalence_string := copy_name;
  end;
end;

@ Here are the new types introduced within that function.  Many Vax/VMS system
services make use of an |item_list| to pass information in and out.  An
|item_list| consists of a number of |item_list| elements, with each element
containing the following fields:

\centerline{\vtop{\offinterlineskip\hrule
  \halign{\vrule#&\ \strut#\hfil\ &\ #\hfil\ &\ #\hfil\ &\vrule#\cr
    height2pt&\omit&\omit&\omit&\cr
    &\hfil Name & \hfil Type & \hfil Usage&\cr
    height2pt&\omit&\omit&\omit&\cr
    \noalign{\hrule}
    height2pt&\omit&\omit&\omit&\cr
    &|buffer_length| & 16-bit word & Size of buffer&\cr
    &|item_code| & unsigned 16-bit word & Code for desired operation&\cr
    &|buffer_address| & Pointer to char & Address of buffer&\cr
    &|ret_len_addr| & Pointer to integer & To receive length of
      translation&\cr
    height2pt&\omit&\omit&\omit&\cr}
  \hrule
}}\yskip

\noindent This structure is overlaid with a single 32-bit integer whose use is
solely to hold the value zero indicating the end of the list.

@<Types in the...@>==
  @!VMS_item_list =
    packed record
      case boolean of
      true: (
        @!buffer_length : sixteen_bits;@/
        @!item_code     : sixteen_bits;@/
        @!buffer_addr   : integer;@/
        @!ret_len_addr  : integer);
      false: (
        @!next_item : integer)
    end;

@* Index.
Pointers to error messages appear here together with the section numbers
where each ident\-i\-fier is used.