~ubuntu-branches/ubuntu/jaunty/texlive-bin/jaunty-security

« back to all changes in this revision

Viewing changes to build/TeX/texk/web2c/dvicopy.web

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Preining
  • Date: 2008-06-26 23:14:59 UTC
  • mfrom: (2.1.30 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080626231459-y02rjsrgtafu83yr
Tags: 2007.dfsg.2-3
add missing source roadmap.fig of roadmap.eps in fontinst documentation
(Closes: #482915) (urgency medium due to RC bug)
(new patch add-missing-fontinst-source)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
% Copyright (C) 1990,95 Peter Breitenlohner (peb@@mppmu.mpg.de)
2
 
%
3
 
% This program is free software; you can redistribute it and/or modify
4
 
% it under the terms of the GNU General Public License as published by
5
 
% the Free Software Foundation; either version 1, or (at your option)
6
 
% any later version.
7
 
%
8
 
% You should have received a copy of the GNU General Public License
9
 
% along with this program; if not, write to the Free Software
10
 
% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
11
 
%
12
 
% Version 0.9 was finished May 21, 1990.
13
 
% Version 1.0 pixel rounding for real devices (August 6, 1990).
14
 
% Version 1.1 major rearrangements for DVIprint (October 7, 1990).
15
 
% Version 1.2 fixed some bugs, page selection (February 13, 1991).
16
 
% Version 1.3 several more changes, command line options,
17
 
%             don't load fonts that are never used (August 25, 1992).
18
 
% Version 1.4 fixed a typo (March 28, 1995).
19
 
% Version 1.5 avoided cur_name_length identifier conflict (October 15, 1995).
20
 
 
21
 
% Here is TeX material that gets inserted after \input webmac
22
 
\def\hang{\hangindent 3em\indent\ignorespaces}
23
 
\font\ninerm=cmr9
24
 
\let\mc=\ninerm % medium caps for names like SAIL
25
 
\def\PASCAL{Pascal}
26
 
\font\logo=manfnt % font used for the METAFONT logo
27
 
\def\MF{{\logo META}\-{\logo FONT}}
28
 
\mathchardef\RA="3221 % right arrow
29
 
 
30
 
\def\(#1){} % this is used to make section names sort themselves better
31
 
\def\9#1{} % this is used for sort keys in the index
32
 
 
33
 
\def\title{DVI\lowercase{copy}} % don't change this line!
34
 
\def\contentspagenumber{1}
35
 
\def\topofcontents{\null
36
 
  \def\titlepage{F} % include headline on the contents page
37
 
  \def\rheader{\mainfont\hfil \contentspagenumber}
38
 
  \vfill
39
 
  \centerline{\titlefont The {\ttitlefont DVIcopy} processor}
40
 
  \vskip 5pt
41
 
  \centerline{Copyright (C) 1990,95 Peter Breitenlohner}
42
 
  \centerline{Distributed under terms of GNU General Public License}
43
 
  \vskip 15pt
44
 
  \centerline{(Version 1.5, October 1995)}
45
 
  \vfill}
46
 
\def\botofcontents{\vfill
47
 
  \centerline{\hsize 5in\baselineskip9pt
48
 
    \vbox{\ninerm\noindent
49
 
    This program was developed at the Max-Planck-Institut f\"ur Physik
50
 
    (Werner-Heisenberg-Institut), Munich, Germany.
51
 
    `\TeX' is a trademark of the American Mathematical Society.
52
 
    `{\logo hijklmnj}\kern1pt' is a trademark of Addison-Wesley
53
 
    Publishing Company.}}}
54
 
\pageno=\contentspagenumber \advance\pageno by 1
55
 
 
56
 
@* Introduction.
57
 
The \.{DVIcopy} utility program copies (selected pages of) binary
58
 
device-independent (``\.{DVI}'') files that are produced by document
59
 
compilers such as \TeX, and replaces all references to characters from
60
 
virtual fonts by the typesetting instructions specified for them in
61
 
binary virtual-font (``\.{VF}'') files.
62
 
This program has two chief purposes: (1)~It can be used as preprocessor
63
 
for existing \.{DVI}-related software in cases where this software is
64
 
unable to handle virtual fonts or (given suitable \.{VF} files) where
65
 
this software cannot handle fonts with more than 128~characters;
66
 
and (2)~it serves as an example of a program that reads \.{DVI} and
67
 
\.{VF} files correctly, for system programmers who are developing
68
 
\.{DVI}-related software.
69
 
 
70
 
Goal number (1) is important since quite a few existing programs have
71
 
to be adapted to the extened capabilities of Version~3 of \TeX\ which
72
 
will require some time. Moreover some existing programs are `as is' and
73
 
the source code is, unfortunately, not available.
74
 
Goal number (2) needs perhaps a bit more explanation. Programs for
75
 
typesetting need to be especially careful about how they do arithmetic; if
76
 
rounding errors accumulate, margins won't be straight, vertical rules
77
 
won't line up, and so on (see the documentaion of \.{DVItype} for more
78
 
details). This program is written as if it were a \.{DVI}-driver for a
79
 
hypothetical typesetting device |out_file|, the output file receiving
80
 
the copy of the input |dvi_file|. In addition all code related to
81
 
|out_file| is concentrated in two chapters at the end of this program
82
 
and quite independent of the rest of the code concerned with the
83
 
decoding of \.{DVI} and \.{VF} files and with font substitutions. Thus
84
 
it should be relatively easy to replace the device dependent code of
85
 
this program by the corresponding code required for a real typesetting
86
 
device. Having this in mind \.{DVItype}'s pixel rounding algorithms are
87
 
included as conditional code not used by \.{DVIcopy}.
88
 
 
89
 
The |banner| and |preamble_comment| strings defined here should be
90
 
changed whenever \.{DVIcopy} gets modified.
91
 
 
92
 
@d banner=='This is DVIcopy, Version 1.5' {printed when the program starts}
93
 
@d title=='DVIcopy' {the name of this program, used in some messages}
94
 
@d copyright=='Copyright (C) 1990,95 Peter Breitenlohner'
95
 
@#
96
 
@d preamble_comment=='DVIcopy 1.5 output from '
97
 
@d comm_length=24 {length of |preamble_comment|}
98
 
@d from_length=6 {length of its |' from '| part}
99
 
 
100
 
@ This program is written in standard \PASCAL, except where it is necessary
101
 
to use extensions; for example, \.{DVIcopy} must read files whose names
102
 
are dynamically specified, and that would be impossible in pure \PASCAL.
103
 
All places where nonstandard constructions are used have been listed in
104
 
the index under ``system dependencies.''
105
 
@!@^system dependencies@>
106
 
 
107
 
One of the extensions to standard \PASCAL\ that we shall deal with is the
108
 
ability to move to a random place in a binary file; another is to
109
 
determine the length of a binary file. Such extensions are not necessary
110
 
for reading \.{DVI} files; since \.{DVIcopy} is (a model for) a
111
 
production program it should, however, be made as efficient as possible
112
 
for a particular system. If \.{DVIcopy} is being used with
113
 
\PASCAL s for which random file positioning is not efficiently available,
114
 
the following definition should be changed from |true| to |false|; in such
115
 
cases, \.{DVIcopy} will not include the optional feature that reads the
116
 
postamble first.
117
 
 
118
 
@d random_reading==true {should we skip around in the file?}
119
 
 
120
 
@ The program begins with a fairly normal header, made up of pieces that
121
 
@^system dependencies@>
122
 
will mostly be filled in later. The \.{DVI} input comes from file
123
 
|dvi_file|, the \.{DVI} output goes to file |out_file|, and messages
124
 
go to \PASCAL's standard |output| file.
125
 
The \.{TFM} and \.{VF} files are defined later since their external
126
 
names are determined dynamically.
127
 
 
128
 
If it is necessary to abort the job because of a fatal error, the program
129
 
calls the `|jump_out|' procedure, which goes to the label |final_end|.
130
 
 
131
 
@d final_end = 9999 {go here to wrap it up}
132
 
 
133
 
@p @t\4@>@<Compiler directives@>@/
134
 
program DVI_copy(@!dvi_file,@!out_file,@!output);
135
 
label final_end;
136
 
const @<Constants in the outer block@>@/
137
 
type @<Types in the outer block@>@/
138
 
var @<Globals in the outer block@>@/
139
 
@<Error handling procedures@>@/
140
 
procedure initialize; {this procedure gets things started properly}
141
 
  var @<Local variables for initialization@>@/
142
 
  begin print_ln(banner);@/
143
 
  print_ln(copyright);
144
 
  print_ln('Distributed under terms of GNU General Public License');@/
145
 
  @<Set initial values@>@/
146
 
  end;
147
 
 
148
 
@ The definition of |max_font_type| should be adapted to the number of
149
 
font types used by the program; the first three values have a fixed
150
 
meaning:  |defined_font=0| indicates that a font has been defined,
151
 
|loaded_font=1| indicates that the \.{TFM} file has been loaded but the
152
 
font has not yet been used, and |vf_font_type=2| indicates a virtual
153
 
font.  Font type values |>=real_font=3| indicate real fonts and
154
 
different font types are used to distinguish various kinds of font files
155
 
(\.{GF} or \.{PK} or \.{PXL}).  \.{DVIcopy} uses |out_font_type=3| for
156
 
fonts that appear in the output \.{DVI} file.
157
 
@!@^font types@>
158
 
 
159
 
@d defined_font=0 {this font has been defined}
160
 
@d loaded_font=1 {this font has been defined and loaded}
161
 
@d vf_font_type=2 {this font is a virtual font}
162
 
@d real_font=3 {smallest font type for real fonts}
163
 
@#
164
 
@d out_font_type=3 {this font appears in the output file}
165
 
@d max_font_type=3
166
 
 
167
 
@ The following parameters can be changed at compile time to extend or
168
 
reduce \.{DVIcopy}'s capacity.
169
 
 
170
 
@d max_select=10 {maximum number of page selection ranges}
171
 
 
172
 
@<Constants...@>=
173
 
@!max_fonts=100; {maximum number of distinct fonts}
174
 
@!max_chars=10000; {maximum number of different characters among all fonts}
175
 
@!max_widths=3000; {maximum number of different characters widths}
176
 
@!max_packets=5000; {maximum number of different characters packets;
177
 
  must be less than 65536}
178
 
@!max_bytes=30000; {maximum number of bytes for characters packets}
179
 
@!max_recursion=10; {\.{VF} files shouldn't recurse beyond this level}
180
 
@!stack_size=100; {\.{DVI} files shouldn't |push| beyond this depth}
181
 
@!terminal_line_length=150; {maximum number of characters input in a single
182
 
  line of input from the terminal}
183
 
@!name_length=50; {a file name shouldn't be longer than this}
184
 
 
185
 
@ As mentioned above, \.{DVIcopy} has two chief purposes: (1)~It produces
186
 
a copy of the input \.{DVI} file with all references to characters from
187
 
virtual fonts replaced by their expansion as specified in the character
188
 
packets of \.{VF} files; and (2)~it serves as an example of a program
189
 
that reads \.{DVI} and \.{VF} files correctly, for system programmers
190
 
who are developing \.{DVI}-related software.
191
 
 
192
 
In fact, a very large section of code (starting with the second chapter
193
 
`Introduction (continued)' and ending with the fifteenth chapter
194
 
`The main program') is used in identical form in \.{DVIcopy} and in
195
 
\.{DVIprint}, a prototype \.{DVI}-driver.  This has been made possible
196
 
mostly by using several \.{WEB} coding tricks, such as not to make the
197
 
resulting \PASCAL\ program inefficient in any way.
198
 
 
199
 
Parts of the program that are needed in \.{DVIprint} but not in
200
 
\.{DVIcopy} are delimited by the codewords `$|device|\ldots|ecived|$';
201
 
these are mostly the pixel rounding algorithms used to convert the
202
 
\.{DVI} units of a \.{DVI} file to the raster units of a real output
203
 
device and have been copied more or less verbatim from \.{DVItype}.
204
 
 
205
 
@d device==@{ {change this to `$\\{device}\equiv\null$' when output
206
 
  for a real device is produced}
207
 
@d ecived==@t@>@} {change this to `$\\{ecived}\equiv\null$' when output
208
 
  for a real device is produced}
209
 
@f device==begin
210
 
@f ecived==end
211
 
 
212
 
@* Introduction (continued).
213
 
On some systems it is necessary to use various integer subrange types
214
 
in order to make \.{\title} efficient; this is true in particular for
215
 
frequently used variables such as loop indices. Consider an integer
216
 
variable |x| with values in the range |0..255|: on most small systems
217
 
|x| should be a one or two byte integer whereas on most large systems
218
 
|x| should be a four byte integer.
219
 
Clearly the author of a program knows best which range of values is
220
 
required for each variable; thus \.{\title} never uses \PASCAL's |integer|
221
 
type. All integer variables are declared as one of the integer subrange
222
 
types defined below as \.{WEB} macros or \PASCAL\ types; these definitions
223
 
can be used without system-dependent changes, provided the signed 32~bit
224
 
integers are a subset of the standard type |integer|, and the compiler
225
 
automatically uses the optimal representation for integer subranges
226
 
(both conditions need not be satisfied for a particular system).
227
 
@^system dependencies@>
228
 
 
229
 
The complementary problem of storing large arrays of integer type
230
 
variables as compactly as possible is addressed differently; here
231
 
\.{\title} uses a \PASCAL\ |type|~declaration for each kind of array
232
 
element.
233
 
 
234
 
Note that the primary purpose of these definitions is optimizations, not
235
 
range checking. All places where optimization for a particular system is
236
 
highly desirable have been listed in the index under ``optimization.''
237
 
@!@^optimization@>
238
 
 
239
 
@d int_32 == integer {signed 32~bit integers}
240
 
 
241
 
@<Types...@>=
242
 
@!int_31 = 0..@"7FFFFFFF; {unsigned 31~bit integer}
243
 
@!int_24u = 0..@"FFFFFF; {unsigned 24~bit integer}
244
 
@!int_24 = -@"800000..@"7FFFFF; {signed 24~bit integer}
245
 
@!int_23 = 0..@"7FFFFF; {unsigned 23~bit integer}
246
 
@!int_16u = 0..@"FFFF; {unsigned 16~bit integer}
247
 
@!int_16 = -@"8000..@"7FFF; {signed 16~bit integer}
248
 
@!int_15 = 0..@"7FFF; {unsigned 15~bit integer}
249
 
@!int_8u = 0..@"FF; {unsigned 8~bit integer}
250
 
@!int_8 = -@"80..@"7F; {signed 8~bit integer}
251
 
@!int_7 = 0..@"7F; {unsigned 7~bit integer}
252
 
 
253
 
@ Some of this code is optional for use when debugging only;
254
 
such material is enclosed between the delimiters |debug| and $|gubed|$.
255
 
Other parts, delimited by |stat| and $|tats|$, are optionally included
256
 
if statistics about \.{\title}'s memory usage are desired.
257
 
 
258
 
@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
259
 
@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
260
 
@f debug==begin
261
 
@f gubed==end
262
 
@#
263
 
@d stat==@{ {change this to `$\\{stat}\equiv\null$'
264
 
  when gathering usage statistics}
265
 
@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
266
 
  when gathering usage statistics}
267
 
@f stat==begin
268
 
@f tats==end
269
 
 
270
 
@ The \PASCAL\ compiler used to develop this program has ``compiler
271
 
directives'' that can appear in comments whose first character is a dollar sign.
272
 
In production versions of \.{\title} these directives tell the compiler that
273
 
@^system dependencies@>
274
 
it is safe to avoid range checks and to leave out the extra code it inserts
275
 
for the \PASCAL\ debugger's benefit, although interrupts will occur if
276
 
there is arithmetic overflow.
277
 
 
278
 
@<Compiler directives@>=
279
 
@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
280
 
@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
281
 
 
282
 
@ Labels are given symbolic names by the following definitions. We insert
283
 
the label `|exit|:' just before the `\ignorespaces|end|\unskip' of a
284
 
procedure in which we have used the `|return|' statement defined below;
285
 
the label `|restart|' is occasionally used at the very beginning of a
286
 
procedure; and the label `|reswitch|' is occasionally used just prior to
287
 
a \&{case} statement in which some cases change the conditions and we wish to
288
 
branch to the newly applicable case.
289
 
Loops that are set up with the \&{loop} construction defined below are
290
 
commonly exited by going to `|done|' or to `|found|' or to `|not_found|',
291
 
and they are sometimes repeated by going to `|continue|'.
292
 
 
293
 
@d exit=10 {go here to leave a procedure}
294
 
@d restart=20 {go here to start a procedure again}
295
 
@d reswitch=21 {go here to start a case statement again}
296
 
@d continue=22 {go here to resume a loop}
297
 
@d done=30 {go here to exit a loop}
298
 
@d found=31 {go here when you've found it}
299
 
@d not_found=32 {go here when you've found something else}
300
 
 
301
 
@ The term |print| is used instead of |write| when this program writes on
302
 
|output|, so that all such output could easily be redirected if desired;
303
 
the term |d_print| is used for conditional output if we are debugging.
304
 
 
305
 
@d print(#)==write(output,#)
306
 
@d print_ln(#)==write_ln(output,#)
307
 
@d new_line==write_ln(output) {start new line}
308
 
@d print_nl(#)==  {print information starting on a new line}
309
 
  begin new_line; print(#);
310
 
  end
311
 
@#
312
 
@d d_print(#)==@!debug print(#) @; @+ gubed
313
 
@d d_print_ln(#)==@! debug print_ln(#) @; @+ gubed
314
 
 
315
 
@ Here are some macros for common programming idioms.
316
 
 
317
 
@d incr(#) == #:=#+1 {increase a variable by unity}
318
 
@d decr(#) == #:=#-1 {decrease a variable by unity}
319
 
@#
320
 
@d Incr_Decr_end(#)==#
321
 
@d Incr(#)==#:=#+Incr_Decr_end {we use |Incr(a)(b)| to increase \dots}
322
 
@d Decr(#)==#:=#-Incr_Decr_end {\dots\ and |Decr(a)(b)| to decrease
323
 
  variable |a| by |b|; this can be optimized for some compilers}
324
 
@#
325
 
@d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
326
 
@d do_nothing == {empty statement}
327
 
@d return == goto exit {terminate a procedure call}
328
 
@f return == nil
329
 
@f loop == xclause
330
 
 
331
 
@ We assume that |case| statements may include a default case that applies
332
 
if no matching label is found. Thus, we shall use constructions like
333
 
@^system dependencies@>
334
 
$$\vbox{\halign{#\hfil\cr
335
 
|case x of|\cr
336
 
1: $\langle\,$code for $x=1\,\rangle$;\cr
337
 
3: $\langle\,$code for $x=3\,\rangle$;\cr
338
 
|othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr
339
 
|endcases|\cr}}$$
340
 
since most \PASCAL\ compilers have plugged this hole in the language by
341
 
incorporating some sort of default mechanism. For example, the compiler
342
 
used to develop \.{WEB} and \TeX\ allows `|others|:' as a default label,
343
 
and other \PASCAL s allow syntaxes like `\ignorespaces|else|\unskip' or
344
 
`\&{otherwise}' or `\\{otherwise}:', etc. The definitions of |othercases|
345
 
and |endcases| should be changed to agree with local conventions. (Of
346
 
course, if no default mechanism is available, the |case| statements of
347
 
this program must be extended by listing all remaining cases.
348
 
Donald~E. Knuth, the author of the \.{WEB} system program \.{TANGLE},
349
 
@^Knuth, Donald Ervin@>
350
 
would have taken the trouble to modify \.{TANGLE} so that such extensions
351
 
were done automatically, if he had not wanted to encourage \PASCAL\
352
 
compiler writers to make this important change in \PASCAL, where it belongs.)
353
 
 
354
 
@d othercases == others: {default for cases not listed explicitly}
355
 
@d endcases == @+end {follows the default case in an extended |case| statement}
356
 
@f othercases == else
357
 
@f endcases == end
358
 
 
359
 
@* The character set.
360
 
Like all programs written with the  \.{WEB} system, \.{\title} can be
361
 
used with any character set. But it uses ASCII code internally, because
362
 
the programming for portable input-output is easier when a fixed internal
363
 
code is used, and because \.{DVI} and \.{VF} files use ASCII code for
364
 
file names and certain other strings.
365
 
 
366
 
The next few sections of \.{\title} have therefore been copied from the
367
 
analogous ones in the \.{WEB} system routines. They have been considerably
368
 
simplified, since \.{\title} need not deal with the controversial
369
 
ASCII codes less than @'40 or greater than @'176.
370
 
If such codes appear in the \.{DVI} file,
371
 
they will be printed as question marks.
372
 
 
373
 
@<Types...@>=
374
 
@!ASCII_code=" ".."~"; {a subrange of the integers}
375
 
 
376
 
@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
377
 
character sets were common, so it did not make provision for lower case
378
 
letters. Nowadays, of course, we need to deal with both upper and lower case
379
 
alphabets in a convenient way, especially in a program like \.{\title}.
380
 
So we shall assume that the \PASCAL\ system being used for \.{\title}
381
 
has a character set containing at least the standard visible characters
382
 
of ASCII code (|"!"| through |"~"|).
383
 
 
384
 
Some \PASCAL\ compilers use the original name |char| for the data type
385
 
associated with the characters in text files, while other \PASCAL s
386
 
consider |char| to be a 64-element subrange of a larger data type that has
387
 
some other name.  In order to accommodate this difference, we shall use
388
 
the name |text_char| to stand for the data type of the characters in the
389
 
output file.  We shall also assume that |text_char| consists of
390
 
the elements |chr(first_text_char)| through |chr(last_text_char)|,
391
 
inclusive. The following definitions should be adjusted if necessary.
392
 
@^system dependencies@>
393
 
 
394
 
@d text_char == char {the data type of characters in text files}
395
 
@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
396
 
@d last_text_char=127 {ordinal number of the largest element of |text_char|}
397
 
 
398
 
@<Types...@>=
399
 
@!text_file=packed file of text_char;
400
 
 
401
 
@ @<Local variables for init...@>=
402
 
@!i:int_16; {loop index for initializations}
403
 
 
404
 
@ The \.{\title} processor converts between ASCII code and
405
 
the user's external character set by means of arrays |xord| and |xchr|
406
 
that are analogous to \PASCAL's |ord| and |chr| functions.
407
 
 
408
 
@<Globals...@>=
409
 
@!xord: array [text_char] of ASCII_code;
410
 
  {specifies conversion of input characters}
411
 
@!xchr: array [0..255] of text_char;
412
 
  {specifies conversion of output characters}
413
 
 
414
 
@ Under our assumption that the visible characters of standard ASCII are
415
 
all present, the following assignment statements initialize the
416
 
|xchr| array properly, without needing any system-dependent changes.
417
 
 
418
 
@<Set init...@>=
419
 
for i:=0 to @'37 do xchr[i]:='?';
420
 
xchr[@'40]:=' ';
421
 
xchr[@'41]:='!';
422
 
xchr[@'42]:='"';
423
 
xchr[@'43]:='#';
424
 
xchr[@'44]:='$';
425
 
xchr[@'45]:='%';
426
 
xchr[@'46]:='&';
427
 
xchr[@'47]:='''';@/
428
 
xchr[@'50]:='(';
429
 
xchr[@'51]:=')';
430
 
xchr[@'52]:='*';
431
 
xchr[@'53]:='+';
432
 
xchr[@'54]:=',';
433
 
xchr[@'55]:='-';
434
 
xchr[@'56]:='.';
435
 
xchr[@'57]:='/';@/
436
 
xchr[@'60]:='0';
437
 
xchr[@'61]:='1';
438
 
xchr[@'62]:='2';
439
 
xchr[@'63]:='3';
440
 
xchr[@'64]:='4';
441
 
xchr[@'65]:='5';
442
 
xchr[@'66]:='6';
443
 
xchr[@'67]:='7';@/
444
 
xchr[@'70]:='8';
445
 
xchr[@'71]:='9';
446
 
xchr[@'72]:=':';
447
 
xchr[@'73]:=';';
448
 
xchr[@'74]:='<';
449
 
xchr[@'75]:='=';
450
 
xchr[@'76]:='>';
451
 
xchr[@'77]:='?';@/
452
 
xchr[@'100]:='@@';
453
 
xchr[@'101]:='A';
454
 
xchr[@'102]:='B';
455
 
xchr[@'103]:='C';
456
 
xchr[@'104]:='D';
457
 
xchr[@'105]:='E';
458
 
xchr[@'106]:='F';
459
 
xchr[@'107]:='G';@/
460
 
xchr[@'110]:='H';
461
 
xchr[@'111]:='I';
462
 
xchr[@'112]:='J';
463
 
xchr[@'113]:='K';
464
 
xchr[@'114]:='L';
465
 
xchr[@'115]:='M';
466
 
xchr[@'116]:='N';
467
 
xchr[@'117]:='O';@/
468
 
xchr[@'120]:='P';
469
 
xchr[@'121]:='Q';
470
 
xchr[@'122]:='R';
471
 
xchr[@'123]:='S';
472
 
xchr[@'124]:='T';
473
 
xchr[@'125]:='U';
474
 
xchr[@'126]:='V';
475
 
xchr[@'127]:='W';@/
476
 
xchr[@'130]:='X';
477
 
xchr[@'131]:='Y';
478
 
xchr[@'132]:='Z';
479
 
xchr[@'133]:='[';
480
 
xchr[@'134]:='\';
481
 
xchr[@'135]:=']';
482
 
xchr[@'136]:='^';
483
 
xchr[@'137]:='_';@/
484
 
xchr[@'140]:='`';
485
 
xchr[@'141]:='a';
486
 
xchr[@'142]:='b';
487
 
xchr[@'143]:='c';
488
 
xchr[@'144]:='d';
489
 
xchr[@'145]:='e';
490
 
xchr[@'146]:='f';
491
 
xchr[@'147]:='g';@/
492
 
xchr[@'150]:='h';
493
 
xchr[@'151]:='i';
494
 
xchr[@'152]:='j';
495
 
xchr[@'153]:='k';
496
 
xchr[@'154]:='l';
497
 
xchr[@'155]:='m';
498
 
xchr[@'156]:='n';
499
 
xchr[@'157]:='o';@/
500
 
xchr[@'160]:='p';
501
 
xchr[@'161]:='q';
502
 
xchr[@'162]:='r';
503
 
xchr[@'163]:='s';
504
 
xchr[@'164]:='t';
505
 
xchr[@'165]:='u';
506
 
xchr[@'166]:='v';
507
 
xchr[@'167]:='w';@/
508
 
xchr[@'170]:='x';
509
 
xchr[@'171]:='y';
510
 
xchr[@'172]:='z';
511
 
xchr[@'173]:='{';
512
 
xchr[@'174]:='|';
513
 
xchr[@'175]:='}';
514
 
xchr[@'176]:='~';
515
 
for i:=@'177 to 255 do xchr[i]:='?';
516
 
 
517
 
@ The following system-independent code makes the |xord| array contain a
518
 
suitable inverse to the information in |xchr|.
519
 
 
520
 
@<Set init...@>=
521
 
for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
522
 
for i:=" " to "~" do xord[xchr[i]]:=i;
523
 
 
524
 
@* Reporting errors to the user.
525
 
The \.{\title} processor does not verify that every single bit read from
526
 
one of its binary input files is meaningful and consistent; there are
527
 
other programs, e.g., \.{DVItype}, \.{TFtoPL}, and \.{VFtoPL}, specially
528
 
designed for that purpose.
529
 
 
530
 
On the other hand, \.{\title} is designed to avoid unpredictable results
531
 
due to undetected arithmetic overflow, or due to violation of integer
532
 
subranges or array bounds under {\it all\/} circumstances. Thus a fair
533
 
amount of checking is done when reading and analyzing the input data,
534
 
even in cases where such checking reduces the efficiency of the program
535
 
to some extent.
536
 
 
537
 
@ A global variable called |history| will contain one of four values
538
 
at the end of every run: |spotless| means that no unusual messages were
539
 
printed; |harmless_message| means that a message of possible interest
540
 
was printed but no serious errors were detected; |error_message| means that
541
 
at least one error was found; |fatal_message| means that the program
542
 
terminated abnormally. The value of |history| does not influence the
543
 
behavior of the program; it is simply computed for the convenience
544
 
of systems that might want to use such information.
545
 
 
546
 
@d spotless=0 {|history| value for normal jobs}
547
 
@d harmless_message=1 {|history| value when non-serious info was printed}
548
 
@d error_message=2 {|history| value when an error was noted}
549
 
@d fatal_message=3 {|history| value when we had to stop prematurely}
550
 
@#
551
 
@d mark_harmless==@t@>@+if history=spotless then history:=harmless_message
552
 
@d mark_error==history:=error_message
553
 
@d mark_fatal==history:=fatal_message
554
 
 
555
 
@<Glob...@>=@!history:spotless..fatal_message; {how bad was this run?}
556
 
 
557
 
@ @<Set init...@>=history:=spotless;
558
 
 
559
 
@ If an input (\.{DVI}, \.{TFM}, \.{VF}, or other) file is badly malformed,
560
 
the whole process must be aborted; \.{\title} will give up, after issuing
561
 
an error message about what caused the error. These messages will, however,
562
 
in most cases just indicate which input file caused the error. One of the
563
 
programs \.{DVItype}, \.{TFtoPL} or \.{VFtoVP} should then be used to
564
 
diagnose the error in full detail.
565
 
 
566
 
Such errors might be discovered inside of subroutines inside of subroutines,
567
 
so a procedure called |jump_out| has been introduced. This procedure, which
568
 
transfers control to the label |final_end| at the end of the program,
569
 
contains the only non-local |@!goto| statement in \.{\title}.
570
 
@^system dependencies@>
571
 
Some \PASCAL\ compilers do not implement non-local |goto| statements. In
572
 
such cases the |goto final_end| in |jump_out| should simply be replaced
573
 
by a call on some system procedure that quietly terminates the program.
574
 
@^system dependencies@>
575
 
 
576
 
@d abort(#)==begin print_ln(' ',#,'.'); jump_out;
577
 
    end
578
 
 
579
 
@<Error handling...@>=
580
 
@<Basic printing procedures@>@;
581
 
procedure close_files_and_terminate; forward;
582
 
@#
583
 
procedure jump_out;
584
 
begin mark_fatal; close_files_and_terminate;
585
 
goto final_end;
586
 
end;
587
 
 
588
 
@ Sometimes the program's behavior is far different from what it should
589
 
be, and \.{\title} prints an error message that is really for the
590
 
\.{\title} maintenance person, not the user. In such cases the program
591
 
says |confusion(|indication of where we are|)|.
592
 
 
593
 
@<Error handling...@>=
594
 
procedure confusion(@!p:pckt_pointer);
595
 
begin print(' !This can''t happen ('); print_packet(p); print_ln(').');
596
 
@.This can't happen@>
597
 
jump_out;
598
 
end;
599
 
 
600
 
@ An overflow stop occurs if \.{\title}'s tables aren't large enough.
601
 
 
602
 
@<Error handling...@>=
603
 
procedure overflow(@!p:pckt_pointer;@!n:int_16u);
604
 
begin print(' !Sorry, ',title,' capacity exceeded ['); print_packet(p);
605
 
@.Sorry, {\title} capacity exceeded@>
606
 
print_ln('=',n:1,'].');
607
 
jump_out;
608
 
end;
609
 
 
610
 
@* Binary data and binary files.
611
 
A detailed description of the \.{DVI} file format can be found in the
612
 
documentation of \TeX, \.{DVItype}, or \.{GFtoDVI}; here we just define
613
 
symbolic names for some of the \.{DVI} command bytes.
614
 
 
615
 
@d set_char_0=0 {typeset character 0 and move right}
616
 
@d set1=128 {typeset a character and move right}
617
 
@d set_rule=132 {typeset a rule and move right}
618
 
@d put1=133 {typeset a character}
619
 
@d put_rule=137 {typeset a rule}
620
 
@d nop=138 {no operation}
621
 
@d bop=139 {beginning of page}
622
 
@d eop=140 {ending of page}
623
 
@d push=141 {save the current positions}
624
 
@d pop=142 {restore previous positions}
625
 
@d right1=143 {move right}
626
 
@d w0=147 {move right by |w|}
627
 
@d w1=148 {move right and set |w|}
628
 
@d x0=152 {move right by |x|}
629
 
@d x1=153 {move right and set |x|}
630
 
@d down1=157 {move down}
631
 
@d y0=161 {move down by |y|}
632
 
@d y1=162 {move down and set |y|}
633
 
@d z0=166 {move down by |z|}
634
 
@d z1=167 {move down and set |z|}
635
 
@d fnt_num_0=171 {set current font to 0}
636
 
@d fnt1=235 {set current font}
637
 
@d xxx1=239 {extension to \.{DVI} primitives}
638
 
@d xxx4=242 {potentially long extension to \.{DVI} primitives}
639
 
@d fnt_def1=243 {define the meaning of a font number}
640
 
@d pre=247 {preamble}
641
 
@d post=248 {postamble beginning}
642
 
@d post_post=249 {postamble ending}
643
 
@#
644
 
@d dvi_id=2 {identifies \.{DVI} files}
645
 
@d dvi_pad=223 {pad bytes at end of \.{DVI} file}
646
 
 
647
 
@ A \.{DVI}, \.{VF}, or \.{TFM} file is a sequence of 8-bit bytes.
648
 
The bytes appear physically in what is called a `|packed file of 0..255|'
649
 
in \PASCAL\ lingo. One, two, three, or four consecutive bytes are often
650
 
interpreted as (signed or unsigned) integers.
651
 
We might as well define the corresponding data types.
652
 
@!@^system dependencies@>
653
 
 
654
 
@<Types...@>=
655
 
@!signed_byte=-@"80..@"7F; {signed one-byte quantity}
656
 
@!eight_bits=0..@"FF; {unsigned one-byte quantity}
657
 
@!signed_pair=-@"8000..@"7FFF; {signed two-byte quantity}
658
 
@!sixteen_bits=0..@"FFFF; {unsigned two-byte quantity}
659
 
@!signed_trio=-@"800000..@"7FFFFF; {signed three-byte quantity}
660
 
@!twentyfour_bits=0..@"FFFFFF; {unsigned three-byte quantity}
661
 
@!signed_quad=int_32; {signed four-byte quantity}
662
 
 
663
 
@ Packing is system dependent, and many \PASCAL\ systems fail to implement
664
 
such files in a sensible way (at least, from the viewpoint of producing
665
 
good production software).  For example, some systems treat all
666
 
byte-oriented files as text, looking for end-of-line marks and such
667
 
things. Therefore some system-dependent code is often needed to deal with
668
 
binary files, even though most of the program in this section of
669
 
\.{\title} is written in standard \PASCAL.
670
 
@^system dependencies@>
671
 
 
672
 
One common way to solve the problem is to consider files of |integer|
673
 
numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to
674
 
a sequence of four bytes $(a,b,c,d)$ using the following code, which
675
 
avoids the controversial integer division of negative numbers:
676
 
$$\vbox{\halign{#\hfil\cr
677
 
|if x>=0 then a:=x div @'100000000|\cr
678
 
|else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr
679
 
\quad|end|\cr
680
 
|x:=x mod @'100000000;|\cr
681
 
|b:=x div @'200000; x:=x mod @'200000;|\cr
682
 
|c:=x div @'400; d:=x mod @'400;|\cr}}$$
683
 
The four bytes are then kept in a buffer and output one by one. (On 36-bit
684
 
computers, an additional division by 16 is necessary at the beginning.
685
 
Another way to separate an integer into four bytes is to use/abuse
686
 
\PASCAL's variant records, storing an integer and retrieving bytes that are
687
 
packed in the same place; {\sl caveat implementor!\/}) It is also desirable
688
 
in some cases to read a hundred or so integers at a time, maintaining a
689
 
larger buffer.
690
 
 
691
 
@ We shall stick to simple \PASCAL\ in the standard version of this program,
692
 
for reasons of clarity, even if such simplicity is sometimes unrealistic.
693
 
 
694
 
@<Types...@>=
695
 
@!byte_file=packed file of eight_bits; {files that contain binary data}
696
 
 
697
 
@ For some operating systems it may be convenient or even necessary to
698
 
close the input files.
699
 
 
700
 
@d close_in(#)==do_nothing {close an input file}
701
 
 
702
 
@ Character packets extracted from \.{VF} files will be stored in a large
703
 
array |byte_mem|. Other packets of bytes, e.g., character packets
704
 
extracted from a \.{GF} or \.{PK} or \.{PXL} file could be stored in the
705
 
same way. A `|pckt_pointer|' variable, which signifies a packet,
706
 
is an index into another array |pckt_start|. The actual sequence of bytes
707
 
in the packet pointed to by |p| appears in positions |pckt_start[p]| to
708
 
|pckt_start[p+1]-1|, inclusive, in |byte_mem|.
709
 
 
710
 
Packets will also be used to store sequences of |ASCII_code|s; in this
711
 
respect the |byte_mem| array is very similar to \TeX's string pool and
712
 
part of the following code has, in fact, been copied more or less
713
 
verbatim from \TeX.
714
 
 
715
 
In other respects the packets resemble the identifiers used by
716
 
\.{TANGLE} and \.{WEAVE} (also stored in an array called |byte_mem|)
717
 
since there is, in general, at most one packet with a given contents;
718
 
thus part of the code below has been adapted from the corresponding code
719
 
in these programs.
720
 
 
721
 
Some \PASCAL\ compilers won't pack integers into a single byte unless the
722
 
integers lie in the range |-128..127|. To accommodate such systems we
723
 
access the array |byte_mem| only via macros that can easily be redefined.
724
 
@^system dependencies@>
725
 
 
726
 
@d bi(#) == # {convert from |eight_bits| to |packed_byte|}
727
 
@d bo(#) == # {convert from |packed_byte| to |eight_bits|}
728
 
 
729
 
@<Types...@>=
730
 
@!packed_byte = eight_bits; {elements of |byte_mem| array}
731
 
@!byte_pointer = 0..max_bytes; {an index into |byte_mem|}
732
 
@!pckt_pointer = 0..max_packets; {an index into |pckt_start|}
733
 
 
734
 
@ The global variable |byte_ptr| points to the first unused location in
735
 
|byte_mem| and |pckt_ptr| points to the first unused location in
736
 
|pckt_start|.
737
 
 
738
 
@<Globals...@>=
739
 
@!byte_mem: packed array [byte_pointer] of packed_byte; {bytes of packets}
740
 
@!pckt_start: array [pckt_pointer] of byte_pointer;
741
 
  {directory into |byte_mem|}
742
 
@!byte_ptr: byte_pointer;
743
 
@!pckt_ptr: pckt_pointer;
744
 
 
745
 
@ Several of the elementary operations with packets are performed using
746
 
\.{WEB} macros instead of \PASCAL\ procedures, because many of the
747
 
operations are done quite frequently and we want to avoid the
748
 
overhead of procedure calls. For example, here is
749
 
a simple macro that computes the length of a packet.
750
 
@.WEB@>
751
 
 
752
 
@d pckt_length(#)==(pckt_start[#+1]-pckt_start[#]) {the number of bytes
753
 
  in packet number \#}
754
 
 
755
 
@ Packets are created by appending bytes to |byte_mem|.
756
 
The |append_byte| macro, defined here, does not check to see if the
757
 
value of |byte_ptr| has gotten too high; this test is supposed to be
758
 
made before |append_byte| is used. There is also a |flush_byte|
759
 
macro, which erases the last byte appended.
760
 
 
761
 
To test if there is room to append |l| more bytes to |byte_mem|,
762
 
we shall write |pckt_room(l)|, which aborts \.{\title} and gives an
763
 
apologetic error message if there isn't enough room.
764
 
 
765
 
@d append_byte(#) == {put byte \# at the end of |byte_mem|}
766
 
begin byte_mem[byte_ptr]:=bi(#); incr(byte_ptr);
767
 
end
768
 
@d flush_byte == decr(byte_ptr) {forget the last byte in |byte_mem|}
769
 
@d pckt_room(#) == {make sure that |byte_mem| hasn't overflowed}
770
 
  if max_bytes-byte_ptr<# then overflow(str_bytes,max_bytes)
771
 
@#
772
 
@d append_one(#) ==
773
 
begin pckt_room(1); append_byte(#);
774
 
end
775
 
 
776
 
@ The length of the current packet is called |cur_pckt_length|:
777
 
 
778
 
@d cur_pckt_length == (byte_ptr - pckt_start[pckt_ptr])
779
 
 
780
 
@ Once a sequence of bytes has been appended to |byte_mem|, it
781
 
officially becomes a packet when the |make_packet| function is called.
782
 
This function returns as its value the identification number of either
783
 
an existing packet with the same contents or, if no such packet exists,
784
 
of the new packet. Thus two packets have the same contents if and only
785
 
if they have the same identification number. In order to locate the
786
 
packet with a given contents, or to find out that no such packet exists,
787
 
we need a hash table. The hash table is kept by the method of simple
788
 
chaining, where the heads of the individual lists appear in the |p_hash|
789
 
array. If |h| is a hash code, the hash table list starts at |p_hash[h]|
790
 
and proceeds through |p_link| pointers.
791
 
 
792
 
@d hash_size=353 {should be prime, must be |>256|}
793
 
 
794
 
@<Types...@>=
795
 
@!hash_code=0..hash_size;
796
 
 
797
 
@ @<Glob...@>=
798
 
@!p_link:array[pckt_pointer] of pckt_pointer; {hash table}
799
 
@!p_hash:array[hash_code] of pckt_pointer;
800
 
 
801
 
@ Initially |byte_mem| and all the hash lists are empty; |empty_packet|
802
 
is the empty packet.
803
 
 
804
 
@d empty_packet=0 {the empty packet}
805
 
@d invalid_packet==max_packets {used when there is no packet}
806
 
 
807
 
@<Set init...@>=
808
 
pckt_ptr:=1; byte_ptr:=1;
809
 
pckt_start[0]:=1; pckt_start[1]:=1;
810
 
for h:=0 to hash_size-1 do p_hash[h]:=0;
811
 
 
812
 
@ @<Local variables for init...@>=
813
 
@!h:hash_code; {index into hash-head arrays}
814
 
 
815
 
@ Here now is the |make_packet| function used to create packets (and
816
 
strings).
817
 
 
818
 
@p function make_packet:pckt_pointer;
819
 
label found;
820
 
var i,@!k:byte_pointer; {indices into |byte_mem|}
821
 
@!h:hash_code; {hash code}
822
 
@!s,@!l:byte_pointer; {start and length of the given packet}
823
 
@!p:pckt_pointer; {where the packet is being sought}
824
 
begin s:=pckt_start[pckt_ptr]; l:=byte_ptr-s; {compute start and length}
825
 
if l=0 then p:=empty_packet
826
 
else  begin @<Compute the packet hash code |h|@>;
827
 
  @<Compute the packet location |p|@>;
828
 
  if pckt_ptr=max_packets then overflow(str_packets,max_packets);
829
 
  incr(pckt_ptr); pckt_start[pckt_ptr]:=byte_ptr;
830
 
  end;
831
 
found:make_packet:=p;
832
 
end;
833
 
 
834
 
@ A simple hash code is used: If the sequence of bytes is
835
 
$b_1b_2\ldots b_n$, its hash value will be
836
 
$$(2^{n-1}b_1+2^{n-2}b_2+\cdots+b_n)\,\bmod\,|hash_size|.$$
837
 
 
838
 
@<Compute the packet hash...@>=
839
 
h:=bo(byte_mem[s]); i:=s+1;
840
 
while i<byte_ptr do
841
 
  begin h:=(h+h+bo(byte_mem[i])) mod hash_size; incr(i);
842
 
  end
843
 
 
844
 
@ If the packet is new, it will be placed in position |p=pckt_ptr|,
845
 
otherwise |p| will point to its existing location.
846
 
 
847
 
@<Compute the packet location...@>=
848
 
p:=p_hash[h];
849
 
while p<>0 do
850
 
  begin if pckt_length(p)=l then
851
 
      @<Compare packet |p| with current packet, |goto found| if equal@>;
852
 
  p:=p_link[p];
853
 
  end;
854
 
p:=pckt_ptr; {the current packet is new}
855
 
p_link[p]:=p_hash[h]; p_hash[h]:=p {insert |p| at beginning of hash list}
856
 
 
857
 
@ @<Compare packet |p|...@>=
858
 
begin i:=s; k:=pckt_start[p];
859
 
while (i<byte_ptr)and(byte_mem[i]=byte_mem[k]) do
860
 
  begin incr(i); incr(k);
861
 
  end;
862
 
if i=byte_ptr then {all bytes agree}
863
 
  begin byte_ptr:=pckt_start[pckt_ptr]; goto found;
864
 
  end;
865
 
end
866
 
 
867
 
@ Some packets are initialized with predefined strings of |ASCII_code|s;
868
 
a few macros permit us to do the initialization with a compact program.
869
 
Since this initialization is done when |byte_mem| is still empty, and
870
 
since |byte_mem| is supposed to be large enough for all the predefined
871
 
strings, |pckt_room| is used only if we are debugging.
872
 
 
873
 
@d pid0(#)==#:=make_packet
874
 
@d pid1(#)==byte_mem[byte_ptr-1]:=bi(#); pid0
875
 
@d pid2(#)==byte_mem[byte_ptr-2]:=bi(#); pid1
876
 
@d pid3(#)==byte_mem[byte_ptr-3]:=bi(#); pid2
877
 
@d pid4(#)==byte_mem[byte_ptr-4]:=bi(#); pid3
878
 
@d pid5(#)==byte_mem[byte_ptr-5]:=bi(#); pid4
879
 
@d pid6(#)==byte_mem[byte_ptr-6]:=bi(#); pid5
880
 
@d pid7(#)==byte_mem[byte_ptr-7]:=bi(#); pid6
881
 
@d pid8(#)==byte_mem[byte_ptr-8]:=bi(#); pid7
882
 
@d pid9(#)==byte_mem[byte_ptr-9]:=bi(#); pid8
883
 
@d pid10(#)==byte_mem[byte_ptr-10]:=bi(#); pid9
884
 
@#
885
 
@d pid_init(#)==
886
 
  @!debug pckt_room(#); @+ gubed @;
887
 
  Incr(byte_ptr)(#)
888
 
@#
889
 
@d id1==pid_init(1); pid1
890
 
@d id2==pid_init(2); pid2
891
 
@d id3==pid_init(3); pid3
892
 
@d id4==pid_init(4); pid4
893
 
@d id5==pid_init(5); pid5
894
 
@d id6==pid_init(6); pid6
895
 
@d id7==pid_init(7); pid7
896
 
@d id8==pid_init(8); pid8
897
 
@d id9==pid_init(9); pid9
898
 
@d id10==pid_init(10); pid10
899
 
 
900
 
@ Here we initialize some strings used as argument of the |overflow| and
901
 
|confusion| procedures.
902
 
 
903
 
@<Initialize predefined strings@>=
904
 
id5("f")("o")("n")("t")("s")(str_fonts);
905
 
id5("c")("h")("a")("r")("s")(str_chars);
906
 
id6("w")("i")("d")("t")("h")("s")(str_widths);
907
 
id7("p")("a")("c")("k")("e")("t")("s")(str_packets);
908
 
id5("b")("y")("t")("e")("s")(str_bytes);
909
 
id9("r")("e")("c")("u")("r")("s")("i")("o")("n")(str_recursion);
910
 
id5("s")("t")("a")("c")("k")(str_stack);
911
 
id10("n")("a")("m")("e")("l")("e")("n")("g")("t")("h")(str_name_length);
912
 
 
913
 
@ @<Glob...@>=
914
 
@!str_fonts,@!str_chars,@!str_widths,@!str_packets,@!str_bytes,
915
 
@!str_recursion,@!str_stack,@!str_name_length:pckt_pointer;
916
 
 
917
 
@ Some packets, e.g., the preamble comments of \.{DVI} and \.{VF} files,
918
 
are needed only temporarily. In such cases |new_packet| is used to
919
 
create a packet (which might duplicate an existing packet) and
920
 
|flush_packet| is used to discard it; the calls to |new_packet| and
921
 
|flush_packet| must occur in balanced pairs, without any intervening
922
 
calls to |make_packet|.
923
 
 
924
 
@p function new_packet: pckt_pointer;
925
 
begin if pckt_ptr=max_packets then overflow(str_packets,max_packets);
926
 
new_packet:=pckt_ptr; incr(pckt_ptr); pckt_start[pckt_ptr]:=byte_ptr;
927
 
end;
928
 
@#
929
 
procedure flush_packet;
930
 
begin decr(pckt_ptr); byte_ptr:=pckt_start[pckt_ptr];
931
 
end;
932
 
 
933
 
@ The |print_packet| procedure prints the contents of a packet; such a
934
 
packets should, of course, consists of a sequence of |ASCII_code|s.
935
 
 
936
 
@<Basic printing...@>=
937
 
procedure print_packet(p:pckt_pointer);
938
 
var k:byte_pointer;
939
 
begin for k:=pckt_start[p] to pckt_start[p+1]-1 do
940
 
  print(xchr[bo(byte_mem[k])]);
941
 
end;
942
 
 
943
 
@ When we interpret a packet we will use two (global or local) variables:
944
 
|cur_loc| will point to the byte to be used next, and |cur_limit| will
945
 
point to the start of the next packet. The macro |pckt_extract| will be
946
 
used to extract one byte; it should, however, never be used with
947
 
|cur_loc>=cur_limit|.
948
 
 
949
 
@d pckt_extract(#) ==
950
 
@!debug if cur_loc>=cur_limit then confusion(str_packets) @+ else @/
951
 
gubed @;
952
 
  begin #:=bo(byte_mem[cur_loc]); incr(cur_loc); @+ end
953
 
 
954
 
@<Globals...@>=
955
 
@!cur_pckt: pckt_pointer; {the current packet}
956
 
@!cur_loc: byte_pointer; {current location in a packet}
957
 
@!cur_limit: byte_pointer; {start of next packet}
958
 
 
959
 
@ We will need routines to extract one, two, three, or four bytes from
960
 
|byte_mem|, from the \.{DVI} file, or from a \.{VF} file and assemble
961
 
them into (signed or unsigned) integers and these routines should be
962
 
optimized for efficiency. Here we define \.{WEB} macros to be used for
963
 
the body of these routines; thus the changes for system dependent
964
 
optimization have to be applied only once.
965
 
@^system dependencies@>
966
 
@^optimization@>
967
 
 
968
 
In addition we demonstrates how these macros can be used to define
969
 
functions that extract one, two, three, or four bytes from a character
970
 
packet and assemble them into signed or unsigned integers (assuming that
971
 
|cur_loc| and |cur_limit| are initialized suitably).
972
 
 
973
 
@d begin_byte(#) ==
974
 
var a:eight_bits;
975
 
begin #(a)
976
 
@d comp_sbyte(#) == if a<128 then #:=a @+ else #:=a-256
977
 
@d comp_ubyte(#) == #:=a
978
 
@f begin_byte == begin
979
 
 
980
 
@p function pckt_sbyte:int_8; {returns the next byte, signed}
981
 
@!begin_byte(pckt_extract); comp_sbyte(pckt_sbyte);
982
 
end;
983
 
@#
984
 
function pckt_ubyte:int_8u; {returns the next byte, unsigned}
985
 
@!begin_byte(pckt_extract); comp_ubyte(pckt_ubyte);
986
 
end;
987
 
 
988
 
@ @d begin_pair(#) ==
989
 
var a,@!b:eight_bits;
990
 
begin #(a); #(b)
991
 
@d comp_spair(#) == if a<128 then #:=a*256+b @+ else #:=(a-256)*256+b
992
 
@d comp_upair(#) == #:=a*256+b
993
 
@f begin_pair == begin
994
 
 
995
 
@p function pckt_spair:int_16; {returns the next two bytes, signed}
996
 
@!begin_pair(pckt_extract); comp_spair(pckt_spair);
997
 
end;
998
 
@#
999
 
function pckt_upair:int_16u; {returns the next two bytes, unsigned}
1000
 
@!begin_pair(pckt_extract); comp_upair(pckt_upair);
1001
 
end;
1002
 
 
1003
 
@ @d begin_trio(#) ==
1004
 
var a,@!b,@!c:eight_bits;
1005
 
begin #(a); #(b); #(c)
1006
 
@d comp_strio(#) ==
1007
 
if a<128 then #:=(a*256+b)*256+c @+ else #:=((a-256)*256+b)*256+c
1008
 
@d comp_utrio(#) == #:=(a*256+b)*256+c
1009
 
@f begin_trio == begin
1010
 
 
1011
 
@p function pckt_strio:int_24; {returns the next three bytes, signed}
1012
 
@!begin_trio(pckt_extract); comp_strio(pckt_strio);
1013
 
end;
1014
 
@#
1015
 
function pckt_utrio:int_24u; {returns the next three bytes, unsigned}
1016
 
@!begin_trio(pckt_extract); comp_utrio(pckt_utrio);
1017
 
end;
1018
 
 
1019
 
@ @d begin_quad(#) ==
1020
 
var a,@!b,@!c,@!d:eight_bits;
1021
 
begin #(a); #(b); #(c); #(d)
1022
 
@d comp_squad(#) ==
1023
 
if a<128 then #:=((a*256+b)*256+c)*256+d
1024
 
else #:=(((a-256)*256+b)*256+c)*256+d
1025
 
@f begin_quad == begin
1026
 
 
1027
 
@p function pckt_squad:int_32; {returns the next four bytes, signed}
1028
 
@!begin_quad(pckt_extract); comp_squad(pckt_squad);
1029
 
end;
1030
 
 
1031
 
@ A similar set of routines is needed for the inverse task of
1032
 
decomposing a \.{DVI} command into a sequence of bytes to be appended
1033
 
to |byte_mem| or, in the case of \.{DVIcopy}, to be written to the
1034
 
output file. Again we define \.{WEB} macros to be used for the body
1035
 
of these routines; thus the changes for system dependent optimization
1036
 
have to be applied only once.
1037
 
@^system dependencies@>
1038
 
@^optimization@>
1039
 
 
1040
 
First, the |pckt_one| outputs one byte, negative values are represented
1041
 
in two's complement notation.
1042
 
 
1043
 
@d begin_one == begin
1044
 
@d comp_one(#) ==
1045
 
if x<0 then Incr(x)(256);
1046
 
#(x)
1047
 
@f begin_one == begin
1048
 
 
1049
 
@p @!device
1050
 
procedure pckt_one(@!x:int_32); {output one byte}
1051
 
@!begin_one; pckt_room(1); comp_one(append_byte);
1052
 
end;
1053
 
ecived
1054
 
 
1055
 
@ The |pckt_two| outputs two bytes, negative values are represented in
1056
 
two's complement notation.
1057
 
 
1058
 
@d begin_two == begin
1059
 
@d comp_two(#) ==
1060
 
if x<0 then Incr(x)(@"10000);
1061
 
#(x div @"100); #(x mod @"100)
1062
 
@f begin_two == begin
1063
 
 
1064
 
@p @!device
1065
 
procedure pckt_two(@!x:int_32); {output two byte}
1066
 
@!begin_two; pckt_room(2); comp_two(append_byte);
1067
 
end;
1068
 
ecived
1069
 
 
1070
 
@ The |pckt_four| procedure outputs four bytes in two's complement
1071
 
notation, without risking arithmetic overflow.
1072
 
 
1073
 
@d begin_four == begin
1074
 
@d comp_four(#) ==
1075
 
if x>=0 then #(x div @"1000000)
1076
 
else  begin Incr(x)(@"40000000); Incr(x)(@"40000000);
1077
 
  #((x div @"1000000) + 128);
1078
 
  end;
1079
 
x:=x mod @"1000000; #(x div @"10000);
1080
 
x:=x mod @"10000; #(x div @"100);
1081
 
#(x mod @"100)
1082
 
@f begin_four == begin
1083
 
 
1084
 
@p procedure pckt_four(@!x:int_32); {output four bytes}
1085
 
@!begin_four; pckt_room(4); comp_four(append_byte);
1086
 
end;
1087
 
 
1088
 
@ Next, the |pckt_char| procedure outputs a |set_char| or \\{set} command
1089
 
or, if |upd=false|, a |put| command.
1090
 
 
1091
 
@d begin_char ==
1092
 
var o:eight_bits; {|set1| or |put1|}
1093
 
begin
1094
 
@d comp_char(#) ==
1095
 
if (not upd)or(res>127)or(ext<>0) then
1096
 
  begin o:=dvi_char_cmd[upd]; {|set1| or |put1|}
1097
 
  if ext<0 then Incr(ext)(@"1000000);
1098
 
  if ext=0 then #(o) @+ else @;
1099
 
    begin if ext<@"100 then #(o+1) @+ else @;
1100
 
      begin if ext<@"10000 then #(o+2) @+ else @;
1101
 
        begin #(o+3); #(ext div @"10000); ext:=ext mod @"10000;
1102
 
        end;
1103
 
      #(ext div @"100); ext:=ext mod @"100;
1104
 
      end;
1105
 
    #(ext);
1106
 
    end;
1107
 
  end;
1108
 
#(res)
1109
 
@f begin_char == begin
1110
 
 
1111
 
@p procedure pckt_char(@!upd:boolean;@!ext:int_32;@!res:eight_bits);
1112
 
  {output \\{set} or |put|}
1113
 
@!begin_char; pckt_room(5); comp_char(append_byte);
1114
 
end;
1115
 
 
1116
 
@ Then, the |pckt_unsigned| procedure outputs a |fnt| or |xxx|
1117
 
command with its first parameter (normally unsigned); a |fnt| command
1118
 
is converted into |fnt_num| whenever this is possible.
1119
 
 
1120
 
@d begin_unsigned == begin
1121
 
@d comp_unsigned(#) ==
1122
 
if (x<@"100)and(x>=0) then
1123
 
  if (o=fnt1)and(x<64) then Incr(x)(fnt_num_0) @+ else #(o)
1124
 
else
1125
 
  begin if (x<@"10000)and(x>=0) then #(o+1) @+ else @;
1126
 
    begin if (x<@"1000000)and(x>=0) then #(o+2) @+ else @;
1127
 
      begin #(o+3);
1128
 
      if x>=0 then #(x div @"1000000)
1129
 
      else  begin Incr(x)(@"40000000); Incr(x)(@"40000000);
1130
 
        #((x div @"1000000) + 128);
1131
 
        end;
1132
 
      x:=x mod @"1000000;
1133
 
      end;
1134
 
    #(x div @"10000); x:=x mod @"10000;
1135
 
    end;
1136
 
  #(x div @"100); x:=x mod @"100;
1137
 
  end;
1138
 
#(x)
1139
 
@f begin_unsigned == begin
1140
 
 
1141
 
@p procedure pckt_unsigned(@!o:eight_bits;@!x:int_32);
1142
 
  {output |fnt_num|, |fnt|, or |xxx|}
1143
 
@!begin_unsigned; pckt_room(5); comp_unsigned(append_byte);
1144
 
end;
1145
 
 
1146
 
@ Finally, the |pckt_signed| procedure outputs a movement (|right|, |w|,
1147
 
|x|, |down|, |y|, or |z|) command with its (signed) parameter.
1148
 
 
1149
 
@d begin_signed ==
1150
 
var xx:int_31; {`absolute value' of |x|}
1151
 
begin
1152
 
@d comp_signed(#) ==
1153
 
if x>=0 then xx:=x @+ else xx:=-(x+1);
1154
 
if xx<@"80 then
1155
 
  begin #(o); @+ if x<0 then Incr(x)(@"100); @+ end
1156
 
else  begin if xx<@"8000 then
1157
 
    begin #(o+1); @+ if x<0 then Incr(x)(@"10000); @+ end
1158
 
  else  begin if xx<@"800000 then
1159
 
      begin #(o+2); @+ if x<0 then Incr(x)(@"1000000); @+ end
1160
 
    else  begin #(o+3);
1161
 
      if x>=0 then #(x div @"1000000)
1162
 
      else  begin x:=@"7FFFFFFF-xx; #((x div @"1000000) + 128); @+ end;
1163
 
      x:=x mod @"1000000;
1164
 
      end;
1165
 
    #(x div @"10000); x:=x mod @"10000;
1166
 
    end;
1167
 
  #(x div @"100); x:=x mod @"100;
1168
 
  end;
1169
 
#(x)
1170
 
@f begin_signed == begin
1171
 
 
1172
 
@p procedure pckt_signed(@!o:eight_bits;@!x:int_32);
1173
 
  {output |right|, |w|, |x|, |down|, |y|, or |z|}
1174
 
@!begin_signed; pckt_room(5); comp_signed(append_byte);
1175
 
end;
1176
 
 
1177
 
@ The |hex_packet| procedure prints the contents of a packet in
1178
 
hexadecimal form.
1179
 
 
1180
 
@<Basic printing...@>=
1181
 
@!debug procedure hex_packet(@!p:pckt_pointer); {prints a packet in hex}
1182
 
var j,@!k,@!l:byte_pointer; {indices into |byte_mem|}
1183
 
@!d:int_8u;
1184
 
begin j:=pckt_start[p]-1; k:=pckt_start[p+1]-1;
1185
 
print_ln(' packet=',p:1,' start=',j+1:1,' length=',k-j:1);
1186
 
for l:=j+1 to k do
1187
 
  begin d:=(bo(byte_mem[l])) div 16;
1188
 
  if d<10 then print(xchr[d+"0"]) @+ else print(xchr[d-10+"A"]);
1189
 
  d:=(bo(byte_mem[l])) mod 16;
1190
 
  if d<10 then print(xchr[d+"0"]) @+ else print(xchr[d-10+"A"]);
1191
 
  if (l=k)or(((l-j) mod 16)=0) then new_line
1192
 
  else if ((l-j) mod 4)=0 then print('  ')
1193
 
  else print(' ');
1194
 
  end;
1195
 
end;
1196
 
gubed
1197
 
 
1198
 
@* File names.
1199
 
The structure of file names is different for different systems; therefore
1200
 
this part of the program will, in most cases, require system dependent
1201
 
modifications. Here we assume that a file name consists of three parts:
1202
 
an area or directory specifying where the file can be found, a name
1203
 
proper and an extension; \.{\title} assumes that these three parts appear
1204
 
in order stated above but this need not be true in all cases.
1205
 
 
1206
 
The font names extracted from \.{DVI} and \.{VF} files consist of an area
1207
 
part and a name proper; these are stored as packets consisting of the
1208
 
length of the area part followed by the area and the name proper.
1209
 
When we print an external font name we simple print the area and the name
1210
 
contained in the `file name packet' without delimiter between them.
1211
 
This may need to be modified for some systems.
1212
 
@^system dependencies@>
1213
 
 
1214
 
@<Basic printing...@>=
1215
 
procedure print_font(@!f:font_number);
1216
 
var p:pckt_pointer; {the font name packet}
1217
 
@!k:byte_pointer; {index into |byte_mem|}
1218
 
@!m:int_31; {font magnification}
1219
 
begin print(' = '); p:=font_name(f);
1220
 
for k:=pckt_start[p]+1 to pckt_start[p+1]-1 do
1221
 
  print(xchr[bo(byte_mem[k])]);
1222
 
m:=round((font_scaled(f)/font_design(f))*out_mag);
1223
 
if m<>1000 then print(' scaled ',m:1);
1224
 
end;
1225
 
 
1226
 
@ Before a font file can be opened for input we must build a string
1227
 
with its external name.
1228
 
 
1229
 
@<Glob...@>=
1230
 
@!cur_name:packed array[1..name_length] of char; {external name,
1231
 
  with no lower case letters}
1232
 
@!l_cur_name:int_15; {this many characters are actually relevant in
1233
 
  |cur_name|}
1234
 
 
1235
 
@ For \.{TFM} and \.{VF} files we just append the apropriate extension
1236
 
to the file name packet; in addition a system dependent area part
1237
 
(usually different for \.{TFM} and \.{VF} files) is prepended if
1238
 
the file name packet contains no area part.
1239
 
@^system dependencies@>
1240
 
 
1241
 
@d append_to_name(#)==
1242
 
  if l_cur_name<name_length then
1243
 
    begin incr(l_cur_name); cur_name[l_cur_name]:=#;
1244
 
    end
1245
 
  else overflow(str_name_length,name_length)
1246
 
@d make_font_name_end(#)==
1247
 
  append_to_name(#[l]); make_name
1248
 
@d make_font_name(#)==
1249
 
  l_cur_name:=0; for l:=1 to # do make_font_name_end
1250
 
 
1251
 
@ For files with character raster data (e.g., \.{GF} or \.{PK} files) the
1252
 
the extension and\slash or area part will in most cases depend on the
1253
 
resolution of the output device (corrected for font magnification).
1254
 
If the special character |res_char| occurs in the extension and\slash or
1255
 
default area, a character string representing the device resolution will
1256
 
be substituted.
1257
 
@^system dependencies@>
1258
 
 
1259
 
@d res_char=='?' {character to be replaced by font resolution}
1260
 
@d res_ASCII="?" {|xord[res_char]|}
1261
 
@#
1262
 
@d append_res_to_name(#)==
1263
 
  begin c:=#;
1264
 
  @!device if c=res_char then
1265
 
    for ll:=n_res_digits downto 1 do append_to_name(res_digits[ll])
1266
 
  else ecived@;@/
1267
 
  append_to_name(c);
1268
 
  end
1269
 
@d make_font_res_end(#)==
1270
 
  append_res_to_name(#[l]); make_name
1271
 
@d make_font_res(#)==
1272
 
  make_res; l_cur_name:=0; for l:=1 to # do make_font_res_end
1273
 
 
1274
 
@ @<Glob...@>=
1275
 
@!device
1276
 
@!f_res:int_16u; {font resolution}
1277
 
@!res_digits:array [1..5] of char;
1278
 
@!n_res_digits:int_7; {number of significant characters in |res_digits|}
1279
 
ecived
1280
 
 
1281
 
@ The |make_res| procedure creates a sequence of characters representing
1282
 
to the font resolution |f_res|.
1283
 
 
1284
 
@p @!device procedure make_res;
1285
 
var r:int_16u;
1286
 
begin n_res_digits:=0; r:=f_res;
1287
 
repeat incr(n_res_digits);
1288
 
  res_digits[n_res_digits]:=xchr["0"+(r mod 10)]; r:=r div 10;
1289
 
until r=0;
1290
 
end;
1291
 
ecived
1292
 
 
1293
 
@ The |make_name| procedure used to build the external file name. The
1294
 
global variable |l_cur_name| contains the length of a default area
1295
 
which has been copied to |cur_name| before |make_name| is called.
1296
 
@^system dependencies@>
1297
 
 
1298
 
@p procedure make_name(@!e:pckt_pointer);
1299
 
var b:eight_bits; {a byte extracted from |byte_mem|}
1300
 
@!n:pckt_pointer; {file name packet}
1301
 
@!cur_loc,@!cur_limit:byte_pointer; {indices into |byte_mem|}
1302
 
@!device
1303
 
@!ll:int_15; {loop index}
1304
 
ecived@;@/
1305
 
@!c:char; {a character to be appended to |cur_name|}
1306
 
begin n:=font_name(cur_fnt);
1307
 
cur_loc:=pckt_start[n]; cur_limit:=pckt_start[n+1];
1308
 
pckt_extract(b); {length of area part}
1309
 
if b>0 then l_cur_name:=0;
1310
 
while cur_loc<cur_limit do
1311
 
  begin pckt_extract(b);
1312
 
  if (b>="a")and(b<="z") then Decr(b)("a"-"A"); {convert to upper case}
1313
 
  append_to_name(xchr[b]);
1314
 
  end;
1315
 
cur_loc:=pckt_start[e]; cur_limit:=pckt_start[e+1];
1316
 
while cur_loc<cur_limit do
1317
 
  begin pckt_extract(b); append_res_to_name(xchr[b]);
1318
 
  end;
1319
 
while l_cur_name<name_length do
1320
 
  begin incr(l_cur_name); cur_name[l_cur_name]:=' ';
1321
 
  end;
1322
 
end;
1323
 
 
1324
 
@* Font data.
1325
 
\.{DVI} file format does not include information about character widths, since
1326
 
that would tend to make the files a lot longer. But a program that reads
1327
 
a \.{DVI} file is supposed to know the widths of the characters that appear
1328
 
in \\{set\_char} commands. Therefore \.{\title} looks at the font metric
1329
 
(\.{TFM}) files for the fonts that are involved.
1330
 
@.TFM {\rm files}@>
1331
 
 
1332
 
The character-width data appears also in other files (e.g., in \.{VF} files
1333
 
or in \.{GF} and \.{PK} files that specify bit patterns for digitized
1334
 
characters); thus, it is usually possible for \.{DVI} reading programs
1335
 
to get by with accessing only one file per font. For \.{VF} reading
1336
 
programs there is, however, a problem: (1)~when reading the character
1337
 
packets from a \.{VF} file the \.{TFM} width for its local fonts should
1338
 
be known in order to analyze and optimize the packets (e.g., determine
1339
 
if a packet must indeed be enclosed with |push| and |pop| as implied by
1340
 
the \.{VF} format); and (2)~ in order to avoid infinite recursion such
1341
 
programs must not try to read a \.{VF} file for a font before a
1342
 
character from that font is actually used. Thus \.{\title} reads the
1343
 
\.{TFM} file whenever a new font is encountered and delays the decision
1344
 
whether this is a virtual font or not.
1345
 
 
1346
 
@ First of all we need to know for each font~|f| such things as its
1347
 
external name, design and scaled size, and the approximate size of
1348
 
inter-word spaces. In addition we need to know the range |bc..ec| of
1349
 
valid characters for this font, and for each character~|c| in~|f|  we
1350
 
need to know if this character exists and if so what is the width of~|c|.
1351
 
Depending on the font type of~|f| we may want to know a few other things
1352
 
about character~|c| in~|f| such as the character packet from a \.{VF}
1353
 
file or the raster data from a \.{PK} file.
1354
 
@^font types@>
1355
 
 
1356
 
In \.{\title} we want to be able to handle the full range
1357
 
|@t$-2^{31}$@><=c<@t$2^{31}$@>| of character codes; each character code
1358
 
is decomposed into a character residue |0<=res<256| and character
1359
 
extension |@t$-2^{23}$@><=ext<@t$2^{23}$@>| such that |c=256*ext+res|.
1360
 
At present \.{VFtoVP}, \.{VPtoVF}, and the standard version of \TeX\ use
1361
 
only characters in the range |0<=c<256| (i.e., |ext=0|), there are,
1362
 
however, extensions of \TeX\ which use characters with |ext<>0|.
1363
 
In any case characters with |ext<>0| will be used rather infrequently
1364
 
and we want to handle this possibility without too much overhead.
1365
 
 
1366
 
Some of the data for each character~|c| depend only on its residue:
1367
 
first of all its width and escapement; others, such as \.{VF} packets or
1368
 
raster data will also depend on its extension. The later will be stored
1369
 
as packets in |byte_mem|, and the packets for characters with the same
1370
 
residue but different extension will be chained.
1371
 
 
1372
 
Thus we have to maintain several variables for each character
1373
 
residue~|bc<=res<=ec| from each font~|f|; we store each type of variable
1374
 
in a large array such that the array index |font_chars(f)+res| points to
1375
 
the value for characters with residue |res| from font~|f|.
1376
 
 
1377
 
@ Quite often a particular width value is shared by several characters in
1378
 
a font or even by characters from different fonts; the later will
1379
 
probably occur in particular for virtual fonts and the local fonts used
1380
 
by them. Thus the array |widths| is used to store all different \.{TFM}
1381
 
width values of all legal characters in all fonts; a variable of type
1382
 
|width_pointer| is an index into |widths| or is zero if a characters does
1383
 
not exist.
1384
 
 
1385
 
In order to locate a given width value we use again a hash
1386
 
table with simple chaining; this time the heads of the individual lists
1387
 
appear in the |w_hash| array and the lists proceed through |w_link|
1388
 
pointers.
1389
 
 
1390
 
@<Types...@>=
1391
 
@!width_pointer=0..max_widths; {an index into |widths|}
1392
 
 
1393
 
@ @<Glob...@>=
1394
 
@!widths:array[width_pointer] of int_32; {the different width values}
1395
 
@!w_link:array[width_pointer] of width_pointer; {hash table}
1396
 
@!w_hash:array[hash_code] of width_pointer;
1397
 
@!n_widths:width_pointer; {first unoccupied position in |widths|}
1398
 
 
1399
 
@ Initially the |widths| array and all the hash lists are empty, except
1400
 
for one entry: the width value zero; in addition we set |widths[0]:=0|.
1401
 
 
1402
 
@d invalid_width=0 {width pointer for invalid characters}
1403
 
@d zero_width=1 {a width pointer to the value zero}
1404
 
 
1405
 
@<Set init...@>=
1406
 
w_hash[0]:=1; w_link[1]:=0; widths[0]:=0; widths[1]:=0; n_widths:=2;
1407
 
for h:=1 to hash_size-1 do w_hash[h]:=0;
1408
 
 
1409
 
@ The |make_width| function returns an index into |widths| and, if
1410
 
necessary, adds a new width value; thus two characters will have the
1411
 
same |width_pointer| if and only if their widths agree.
1412
 
 
1413
 
@p function make_width(@!w:int_32):width_pointer;
1414
 
label found;
1415
 
var h:hash_code; {hash code}
1416
 
@!p:width_pointer; {where the identifier is being sought}
1417
 
@!x:int_16; {intermediate value}
1418
 
begin widths[n_widths]:=w;
1419
 
@<Compute the width hash code |h|@>;
1420
 
@<Compute the width location |p|, |goto| found unless the value is new@>;
1421
 
if n_widths=max_widths then overflow(str_widths,max_widths);
1422
 
incr(n_widths);
1423
 
found:make_width:=p;
1424
 
end;
1425
 
 
1426
 
@ A simple hash code is used: If the width value consists of the four
1427
 
bytes $b_0b_1b_2b_3$, its hash value will be
1428
 
$$(8*b_0+4*b_1+2*b_2+b_3)\,\bmod\,|hash_size|.$$
1429
 
 
1430
 
@<Compute the width hash...@>=
1431
 
if w>=0 then x:=w div @"1000000
1432
 
else  begin w:=w+@"40000000; w:=w+@"40000000; x:=(w div @"1000000)+@"80;
1433
 
  end;
1434
 
w:=w mod @"1000000; x:=x+x+(w div @"10000);
1435
 
w:=w mod @"10000; x:=x+x+(w div @"100);
1436
 
h:=(x+x+(w mod @"100)) mod hash_size
1437
 
 
1438
 
@ If the width is new, it has been placed into position |p=n_widths|,
1439
 
otherwise |p| will point to its existing location.
1440
 
 
1441
 
@<Compute the width location...@>=
1442
 
p:=w_hash[h];
1443
 
while p<>0 do
1444
 
  begin if widths[p]=widths[n_widths] then goto found;
1445
 
  p:=w_link[p];
1446
 
  end;
1447
 
p:=n_widths; {the current width is new}
1448
 
w_link[p]:=w_hash[h]; w_hash[h]:=p {insert |p| at beginning of hash list}
1449
 
 
1450
 
@ The |char_widths| array is used to store the |width_pointer|s for all
1451
 
different characters among all fonts.  The |char_packets| array is used
1452
 
to store the |pckt_pointer|s for all different characters among all
1453
 
fonts; they can point to character packets from \.{VF} files or, e.g.,
1454
 
raster packets from \.{PK} files.
1455
 
 
1456
 
@<Types...@>=
1457
 
@!char_offset=-255..max_chars; {|char_pointer| offset for a font}
1458
 
@!char_pointer=0..max_chars; {index into |char_widths| or similar arrays}
1459
 
 
1460
 
@ @<Glob...@>=
1461
 
@!char_widths:array[char_pointer] of width_pointer; {width pointers}
1462
 
@!char_packets:array[char_pointer] of pckt_pointer; {packet pointers}
1463
 
@!n_chars:char_pointer; {first unused position in |char_widths|}
1464
 
 
1465
 
@ @<Set init...@>=
1466
 
n_chars:=0;
1467
 
 
1468
 
@ The current number of known fonts is |nf|; each known font has an
1469
 
internal number |f|, where |0<=f<nf|.  For the moment we need for each
1470
 
known font:  |font_check|, |font_scaled|, |font_design|, |font_name|,
1471
 
|font_bc|, |font_ec|, |font_chars|, and |font_type|.  Here |font_scaled|
1472
 
and |font_design| are measured in \.{DVI} units and |font_chars| is of
1473
 
type |char_offset|:  the width pointer for character~|c| of the font is
1474
 
stored in |char_widths[char_offset+c]| (for |font_bc<=c<=font_ec|).
1475
 
Lateron we will need additional information depending on the font type:
1476
 
\.{VF} or real (\.{GF}, \.{PK}, or \.{PXL}).
1477
 
 
1478
 
@<Types...@>=
1479
 
@!f_type=defined_font..max_font_type; {type of a font}
1480
 
@!font_number=0..max_fonts;
1481
 
 
1482
 
@ @<Glob...@>=
1483
 
@!nf:font_number;
1484
 
 
1485
 
@ These data are stored in several arrays and we use \.{WEB} macros
1486
 
to access the various fields. Thus it would be simple to store the
1487
 
data in an array of record structures and adapt the \.{WEB} macros
1488
 
accordingly.
1489
 
 
1490
 
We will say, e.g., |font_name(f)| for the name field of font~|f|, and
1491
 
|font_width(f)(c)| for the width pointer of character~|c| in font~|f|
1492
 
and |font_packet(f)(c)| for its character packet (this character
1493
 
exists provided |font_bc(f)<=c<=font_ec(f)| and
1494
 
|font_width(f)(c)<>invalid_width|). The actual width of character~|c| in
1495
 
font~|f| is stored in |widths[font_width(f)(c)]|.
1496
 
 
1497
 
@d font_check(#)==fnt_check[#] {checksum}
1498
 
@d font_scaled(#)==fnt_scaled[#] {scaled or `at' size}
1499
 
@d font_design(#)==fnt_design[#] {design size}
1500
 
@d font_name(#)==fnt_name[#] {area plus name packet}
1501
 
@d font_bc(#)==fnt_bc[#] {first character}
1502
 
@d font_ec(#)==fnt_ec[#] {last character}
1503
 
@d font_chars(#)==fnt_chars[#] {character info offset}
1504
 
@d font_type(#)==fnt_type[#] {type of this font}
1505
 
@d font_font(#)==fnt_font[#] {use depends on |font_type|}
1506
 
@#
1507
 
@d font_width_end(#)==#]
1508
 
@d font_width(#)==char_widths[font_chars(#)+font_width_end
1509
 
@d font_packet(#)==char_packets[font_chars(#)+font_width_end
1510
 
 
1511
 
@<Glob...@>=
1512
 
@!fnt_check:array [font_number] of int_32; {checksum}
1513
 
@!fnt_scaled:array [font_number] of int_31; {scaled size}
1514
 
@!fnt_design:array [font_number] of int_31; {design size}
1515
 
@!device @<Declare device dependent font data arrays@>@; @+ ecived @; @/
1516
 
@!fnt_name:array [font_number] of pckt_pointer; {pointer to area plus
1517
 
  name packet}
1518
 
@!fnt_bc:array [font_number] of eight_bits; {first character}
1519
 
@!fnt_ec:array [font_number] of eight_bits; {last character}
1520
 
@!fnt_chars:array [font_number] of char_offset; {character info offset}
1521
 
@!fnt_type:array [font_number] of f_type; {type of font}
1522
 
@!fnt_font:array [font_number] of font_number; {use depends on |font_type|}
1523
 
 
1524
 
@ @d invalid_font==max_fonts {used when there is no valid font}
1525
 
 
1526
 
@<Set init...@>=
1527
 
@!device @<Initialize device dependent font data@>@; @+ ecived @;@/
1528
 
nf:=0;
1529
 
 
1530
 
@ A \.{VF}, or \.{GF}, or \.{PK} file may contain information for
1531
 
several characters with the same residue but with different extension;
1532
 
all except the first of the corresponding packets in |byte_mem| will
1533
 
contain a pointer to the previous one and |font_packet(f)(res)|
1534
 
identifies the last such packet.
1535
 
 
1536
 
A character packet in |byte_mem| starts with a flag byte
1537
 
$$\hbox{|flag=@"40*ext_flag+@"20*chain_flag+type_flag|}$$
1538
 
with |0<=ext_flag<=3|, |0<=chain_flag<=1|, |0<=type_flag<=@"1F|,
1539
 
followed by |ext_flag| bytes with the character extension for this
1540
 
packet and, if |chain_flag=1|, by a two byte packet pointer to the
1541
 
previous packet for the same font and character residue. The actual
1542
 
character packet follows after these header bytes and the
1543
 
interpretation of the |type_flag| depends on whether this is a \.{VF}
1544
 
packet or a packet for raster data.
1545
 
 
1546
 
The empty packet is interpreted as a special case of a packet with
1547
 
|flag=0|.
1548
 
 
1549
 
@d ext_flag=@"40
1550
 
@d chain_flag=@"20
1551
 
 
1552
 
@<Types...@>=
1553
 
@!type_flag=0..chain_flag-1; {the range of values for the |type_flag|}
1554
 
 
1555
 
@ The global variable |cur_fnt| is the internal font number of the
1556
 
currently selected font, or equals |invalid_font| if no font has
1557
 
been selected; |cur_res| and |cur_ext| are the residue and extension
1558
 
part of the current character code. The type of a character packet
1559
 
located by the |find_packet| function defined below is |cur_type|.
1560
 
While building a character packet for a character, |pckt_ext| and
1561
 
|pckt_res| are the extension and residue of this character; |pckt_dup|
1562
 
indicates whether a packet for this extension exists already.
1563
 
 
1564
 
@<Glob...@>=
1565
 
@!cur_fnt:font_number; {the currently selected font}
1566
 
@!cur_ext:int_24; {the current character extension}
1567
 
@!cur_res:int_8u; {the current character residue}
1568
 
@!cur_type:type_flag; {type of the current character packet}
1569
 
@!pckt_ext:int_24; {character extension for the current character packet}
1570
 
@!pckt_res:int_8u; {character residue for the current character packet}
1571
 
@!pckt_dup:boolean; {is there a previous packet for the same extension?}
1572
 
@!pckt_prev:pckt_pointer; {a previous packet for the same extension}
1573
 
@!pckt_m_msg,@!pckt_s_msg,@!pckt_d_msg:int_7; {counts for various character
1574
 
  packet error messages}
1575
 
 
1576
 
@ @<Set init...@>=
1577
 
cur_fnt:=invalid_font; pckt_m_msg:=0; pckt_s_msg:=0; pckt_d_msg:=0;
1578
 
 
1579
 
@ The |find_packet| functions is used to locate the character packet for
1580
 
the character with residue~|cur_res| and extension~|cur_ext| from
1581
 
font~|cur_fnt| and returns |false| if no packet exists for any extension;
1582
 
otherwise the result is |true| and the global variables |cur_packet|,
1583
 
|cur_type|, |cur_loc|, and |cur_limit| are initialized. In case none of
1584
 
the character packets has the correct extension, the last one in the
1585
 
chain (the one defined first) is used instead and |cur_ext| is changed
1586
 
accordingly.
1587
 
 
1588
 
@p function find_packet:boolean;
1589
 
label found,exit;
1590
 
var p,@!q:pckt_pointer; {current and next packet}
1591
 
@!f:eight_bits; {a flag byte}
1592
 
@!e:int_24; {extension for a packet}
1593
 
begin @<Locate a character packet and |goto found| if found@>;
1594
 
if font_packet(cur_fnt)(cur_res)=invalid_packet then
1595
 
  begin if pckt_m_msg<10 then {stop telling after first 10 times}
1596
 
    begin print_ln('---missing character packet for character ',cur_res:1,
1597
 
@.missing character packet...@>
1598
 
      ' font ',cur_fnt:1);
1599
 
    incr(pckt_m_msg); mark_error;
1600
 
    if pckt_m_msg=10 then print_ln('---further messages suppressed.');
1601
 
    end;
1602
 
  find_packet:=false; return;
1603
 
  end;
1604
 
if pckt_s_msg<10 then {stop telling after first 10 times}
1605
 
  begin print_ln('---substituted character packet with extension ',
1606
 
@.substituted character packet...@>
1607
 
    e:1,' instead of ',cur_ext:1,' for character ',cur_res:1,
1608
 
    ' font ',cur_fnt:1);
1609
 
  incr(pckt_s_msg); mark_error;
1610
 
  if pckt_s_msg=10 then print_ln('---further messages suppressed.');
1611
 
  end;
1612
 
cur_ext:=e;
1613
 
found: cur_pckt:=p; cur_type:=f; find_packet:=true;
1614
 
exit: end;
1615
 
 
1616
 
@ @<Locate a character packet and |goto found| if found@>=
1617
 
q:=font_packet(cur_fnt)(cur_res);
1618
 
while q<>invalid_packet do
1619
 
  begin p:=q; q:=invalid_packet;
1620
 
  cur_loc:=pckt_start[p]; cur_limit:=pckt_start[p+1];
1621
 
  if p=empty_packet then
1622
 
    begin e:=0; f:=0;
1623
 
    end
1624
 
  else  begin pckt_extract(f);
1625
 
    case (f div ext_flag) of
1626
 
    0: e:=0;
1627
 
    1: e:=pckt_ubyte;
1628
 
    2: e:=pckt_upair;
1629
 
    3: e:=pckt_strio;
1630
 
    end; {there are no other cases}
1631
 
    if (f mod ext_flag)>=chain_flag then q:=pckt_upair;
1632
 
    f:=f mod chain_flag;
1633
 
    end;
1634
 
  if e=cur_ext then goto found;
1635
 
  end
1636
 
 
1637
 
@ The |start_packet| procedure is used to create the header bytes of a
1638
 
character packet for the character with residue~|cur_res| and
1639
 
extension~|cur_ext| from font~|cur_fnt|; if a previous such a packet
1640
 
exists, we try to build an exact duplicate, i.e., use the chain field of
1641
 
that previous packet.
1642
 
 
1643
 
@p procedure start_packet(@!t:type_flag);
1644
 
label found,not_found;
1645
 
var p,@!q:pckt_pointer; {current and next packet}
1646
 
@!f:int_8u; {a flag byte}
1647
 
@!e:int_32; {extension for a packet}
1648
 
@!cur_loc: byte_pointer; {current location in a packet}
1649
 
@!cur_limit: byte_pointer; {start of next packet}
1650
 
begin @<Locate a character packet and |goto found| if found@>;
1651
 
q:=font_packet(cur_fnt)(cur_res); pckt_dup:=false; goto not_found;
1652
 
found: pckt_dup:=true; pckt_prev:=p;
1653
 
not_found: pckt_ext:=cur_ext; pckt_res:=cur_res; pckt_room(6);
1654
 
@!debug if byte_ptr<>pckt_start[pckt_ptr] then confusion(str_packets);
1655
 
gubed @;@/
1656
 
if q=invalid_packet then f:=t @+ else f:=t+chain_flag;
1657
 
e:=cur_ext;
1658
 
if e<0 then Incr(e)(@"1000000);
1659
 
if e=0 then append_byte(f) @+ else @;
1660
 
  begin if e<@"100 then append_byte(f+ext_flag) @+ else @;
1661
 
    begin if e<@"10000 then append_byte(f+ext_flag+ext_flag) @+ else @;
1662
 
      begin append_byte(f+ext_flag+ext_flag+ext_flag);
1663
 
      append_byte(e div @"10000); e:=e mod @"10000;
1664
 
      end;
1665
 
    append_byte(e div @"100); e:=e mod @"100;
1666
 
    end;
1667
 
  append_byte(e);
1668
 
  end;
1669
 
if q<>invalid_packet then
1670
 
  begin append_byte(q div @"100); append_byte(q mod @"100);
1671
 
  end;
1672
 
end;
1673
 
 
1674
 
@ The |build_packet| procedure is used to finish a character packet.
1675
 
If a previous packet for the same character extension exists, the new
1676
 
one is discarded; if the two packets are identical, as it occasionally
1677
 
occurs for raster files, this is done without an error message.
1678
 
 
1679
 
@p procedure build_packet;
1680
 
var k,@!l:byte_pointer; {indices into |byte_mem|}
1681
 
begin if pckt_dup then
1682
 
  begin k:=pckt_start[pckt_prev+1]; l:=pckt_start[pckt_ptr];
1683
 
  if (byte_ptr-l)<>(k-pckt_start[pckt_prev]) then pckt_dup:=false;
1684
 
  while pckt_dup and(byte_ptr>l) do
1685
 
    begin flush_byte; decr(k);
1686
 
    if byte_mem[byte_ptr]<>byte_mem[k] then pckt_dup:=false;
1687
 
    end;
1688
 
  if (not pckt_dup)and(pckt_d_msg<10) then {stop telling after first 10 times}
1689
 
    begin print('---duplicate packet for character ',pckt_res:1);
1690
 
@.duplicate packet for character...@>
1691
 
    if pckt_ext<>0 then print('.',pckt_ext:1);
1692
 
    print_ln(' font ',cur_fnt:1);
1693
 
    incr(pckt_d_msg); mark_error;
1694
 
    if pckt_d_msg=10 then print_ln('---further messages suppressed.');
1695
 
    end;
1696
 
  byte_ptr:=l;
1697
 
  end
1698
 
else font_packet(cur_fnt)(pckt_res):=make_packet;
1699
 
end;
1700
 
 
1701
 
@* Defining fonts.
1702
 
A detailed description of the \.{TFM} file format can be found in the
1703
 
documentation of \TeX, \MF, or \.{TFtoPL}.  In order to read \.{TFM}
1704
 
files the program uses the binary file variable |tfm_file|.
1705
 
 
1706
 
@<Glob...@>=
1707
 
@!tfm_file:byte_file; {a \.{TFM} file}
1708
 
@!tfm_ext:pckt_pointer; {extension for \.{TFM} files}
1709
 
 
1710
 
@ @<Initialize predefined strings@>=
1711
 
id4(".")("T")("F")("M")(tfm_ext); {file name extension for \.{TFM} files}
1712
 
 
1713
 
@ If no font directory has been specified, \.{\title} is supposed to use
1714
 
the default \.{TFM} directory, which is a system-dependent place where
1715
 
the \.{TFM} files for standard fonts are kept.
1716
 
The string variable |TFM_default_area| contains the name of this area.
1717
 
@^system dependencies@>
1718
 
 
1719
 
@d TFM_default_area_name=='TeXfonts:' {change this to the correct name}
1720
 
@d TFM_default_area_name_length=9 {change this to the correct length}
1721
 
 
1722
 
@<Glob...@>=
1723
 
@!TFM_default_area:packed array[1..TFM_default_area_name_length] of char;
1724
 
 
1725
 
@ @<Set init...@>=
1726
 
TFM_default_area:=TFM_default_area_name;
1727
 
 
1728
 
@ If a \.{TFM} file is badly malformed, we say |bad_font|; for a \.{TFM}
1729
 
file the |bad_tfm| procedure is used to give an error message which
1730
 
refers the user to \.{TFtoPL} and \.{PLtoTF}, and terminates \.{\title}.
1731
 
 
1732
 
@<Error handling...@>=
1733
 
procedure bad_tfm;
1734
 
begin print('Bad TFM file'); print_font(cur_fnt); print_ln('!');
1735
 
@.Bad TFM file@>
1736
 
abort('Use TFtoPL/PLtoTF to diagnose and correct the problem');
1737
 
@.Use TFtoPL/PLtoTF@>
1738
 
end;
1739
 
@#
1740
 
procedure bad_font;
1741
 
begin new_line;
1742
 
case font_type(cur_fnt) of
1743
 
  defined_font: confusion(str_fonts);
1744
 
  loaded_font: bad_tfm;
1745
 
  @<Cases for |bad_font|@>@;@/
1746
 
  end; {there are no other cases}
1747
 
end;
1748
 
 
1749
 
@ To prepare |tfm_file| for input we |reset| it.
1750
 
 
1751
 
@<TFM: Open |tfm_file|@>=
1752
 
make_font_name(TFM_default_area_name_length)(TFM_default_area)(tfm_ext);
1753
 
reset(tfm_file,cur_name);
1754
 
if eof(tfm_file) then
1755
 
@^system dependencies@>
1756
 
  abort('---not loaded, TFM file can''t be opened!')
1757
 
@.TFM file can\'t be opened@>
1758
 
 
1759
 
@ It turns out to be convenient to read four bytes at a time, when we
1760
 
are inputting from \.{TFM} files. The input goes into global variables
1761
 
|tfm_b0|, |tfm_b1|, |tfm_b2|, and |tfm_b3|, with |tfm_b0| getting the
1762
 
first byte and |tfm_b3| the fourth.
1763
 
 
1764
 
@<Glob...@>=
1765
 
@!tfm_b0,@!tfm_b1,@!tfm_b2,@!tfm_b3: eight_bits; {four bytes input at once}
1766
 
 
1767
 
@ Reading a \.{TFM} file should be done as efficient as possible for a
1768
 
particular system; on many systems this means that a large number of
1769
 
bytes from |tfm_file| is read into a buffer and will then be extracted
1770
 
from that buffer. In order to simplify such system dependent changes
1771
 
we use the \.{WEB} macro |tfm_byte| to extract the next \.{TFM} byte;
1772
 
this macro and |eof(tfm_file)| are used only in the |read_tfm_word|
1773
 
procedure which sets |tfm_b0| through |tfm_b3| to the next four bytes
1774
 
in the current \.{TFM} file. Here we give simple minded definitions in
1775
 
terms of standard \PASCAL.
1776
 
@^system dependencies@>
1777
 
@^optimization@>
1778
 
 
1779
 
@d tfm_byte(#)==read(tfm_file,#) {read next \.{TFM} byte}
1780
 
 
1781
 
@p procedure read_tfm_word;
1782
 
begin tfm_byte(tfm_b0); tfm_byte(tfm_b1);
1783
 
tfm_byte(tfm_b2); tfm_byte(tfm_b3);
1784
 
if eof(tfm_file) then bad_font;
1785
 
end;
1786
 
 
1787
 
@ Here are three procedures used to check the consistency of font files:
1788
 
First, the |check_check_sum| procedure compares two check sum values: a
1789
 
warning is given if they differ and are both non-zero; if the second
1790
 
value is not zero it may replace the first one.
1791
 
Next, the |check_design_size| procedure compares two design size
1792
 
values: a warning is given if they differ by more than a small amount.
1793
 
Finally, the |check_width| function compares the character width value
1794
 
for character |cur_res| read from a \.{VF} or raster file for font
1795
 
|cur_fnt| with the value previously read from the \.{TFM} file and
1796
 
returns the width pointer for that value; a warning is given if the two
1797
 
values differ.
1798
 
 
1799
 
@p procedure check_check_sum(@!c:int_32;@!u:boolean);
1800
 
  {compare |font_check(cur_fnt)| with |c|}
1801
 
begin if (c<>font_check(cur_fnt))and(c<>0) then
1802
 
  begin
1803
 
  if font_check(cur_fnt)<>0 then
1804
 
    begin new_line; print_ln('---beware: check sums do not agree!   (',
1805
 
@.beware: check sums do not agree@>
1806
 
@.check sums do not agree@>
1807
 
      c:1,' vs. ',font_check(cur_fnt):1,')');
1808
 
    mark_harmless;
1809
 
    end;
1810
 
  if u then font_check(cur_fnt):=c;
1811
 
  end;
1812
 
end;
1813
 
@#
1814
 
procedure check_design_size(@!d:int_32);
1815
 
  {compare |font_design(cur_fnt)| with |d|}
1816
 
begin if abs(d-font_design(cur_fnt))>2 then
1817
 
  begin new_line; print_ln('---beware: design sizes do not agree!   (',
1818
 
@.beware: design sizes do not agree@>
1819
 
@.design sizes do not agree@>
1820
 
    d:1,' vs. ',font_design(cur_fnt):1,')');
1821
 
  mark_error;
1822
 
  end;
1823
 
end;
1824
 
@#
1825
 
function check_width(w:int_32):width_pointer;
1826
 
  {compare |widths[font_width(cur_fnt)(cur_res)]| with |w|}
1827
 
var wp:width_pointer; {pointer to \.{TFM} width value}
1828
 
begin if (cur_res>=font_bc(cur_fnt))and(cur_res<=font_ec(cur_fnt)) then
1829
 
  wp:=font_width(cur_fnt)(cur_res)
1830
 
else wp:=invalid_width;
1831
 
if wp=invalid_width then
1832
 
  begin print_nl('Bad char ',cur_res:1);
1833
 
@.Bad char c@>
1834
 
  if cur_ext<>0 then print('.',cur_ext:1);
1835
 
  print(' font ',cur_fnt:1); print_font(cur_fnt);
1836
 
  abort(' (compare TFM file)');
1837
 
  end;
1838
 
if w<>widths[wp] then
1839
 
  begin new_line; print_ln('---beware: char widths do not agree!   (',
1840
 
@.beware: char widths do not agree@>
1841
 
@.char widths do not agree@>
1842
 
    w:1,' vs. ',widths[wp]:1,')');
1843
 
  mark_error;
1844
 
  end;
1845
 
check_width:=wp;
1846
 
end;
1847
 
 
1848
 
@ The |load_font| procedure reads the \.{TFM} file for a font and puts
1849
 
the data extracted into position |cur_fnt| of the font data arrays.
1850
 
 
1851
 
@p procedure load_font; {reads a \.{TFM} file}
1852
 
var l:int_16; {loop index}
1853
 
@!p:char_pointer; {index into |char_widths|}
1854
 
@!q:width_pointer; {index into |widths|}
1855
 
@!bc,@!ec:int_15; {first and last character in this font}
1856
 
@!lh:int_15; {length of header in four byte words}
1857
 
@!nw:int_15; {number of words in width table}
1858
 
@!w:int_32; {a four byte integer}
1859
 
@<Variables for scaling computation@>@;
1860
 
begin print('TFM: font ',cur_fnt:1); print_font(cur_fnt);
1861
 
font_type(cur_fnt):=loaded_font;
1862
 
@<TFM: Open |tfm_file|@>;
1863
 
@<TFM: Read past the header data@>;
1864
 
@<TFM: Store character-width indices@>;
1865
 
@<TFM: Read and convert the width values@>;
1866
 
@<TFM: Convert character-width indices to character-width pointers@>;
1867
 
close_in(tfm_file);
1868
 
@!device @<Initialize device dependent data for a font@>@; @+ ecived @; @/
1869
 
d_print(' loaded at ',font_scaled(cur_fnt):1,' DVI units');
1870
 
print_ln('.');
1871
 
end;
1872
 
 
1873
 
@ @<Glob...@>=
1874
 
@!tfm_conv:real; {\.{DVI} units per absolute \.{TFM} unit}
1875
 
 
1876
 
@ We will use the following \.{WEB} macros to construct integers from
1877
 
two or four of the four bytes read by |read_tfm_word|.
1878
 
@^system dependencies@>
1879
 
 
1880
 
@d tfm_b01(#)== {|tfm_b0..tfm_b1| as non-negative integer}
1881
 
if tfm_b0>127 then bad_font
1882
 
else #:=tfm_b0*256+tfm_b1
1883
 
@d tfm_b23(#)== {|tfm_b2..tfm_b3| as non-negative integer}
1884
 
if tfm_b2>127 then bad_font
1885
 
else #:=tfm_b2*256+tfm_b3
1886
 
@d tfm_squad(#)== {|tfm_b0..tfm_b3| as signed integer}
1887
 
if tfm_b0<128 then #:=((tfm_b0*256+tfm_b1)*256+tfm_b2)*256+tfm_b3
1888
 
else #:=(((tfm_b0-256)*256+tfm_b1)*256+tfm_b2)*256+tfm_b3
1889
 
@d tfm_uquad== {|tfm_b0..tfm_b3| as unsigned integer}
1890
 
(((tfm_b0*256+tfm_b1)*256+tfm_b2)*256+tfm_b3)
1891
 
 
1892
 
@<TFM: Read past the header data@>=
1893
 
read_tfm_word; tfm_b23(lh);
1894
 
read_tfm_word; tfm_b01(bc); tfm_b23(ec);
1895
 
if ec<bc then
1896
 
  begin bc:=1; ec:=0;
1897
 
  end
1898
 
else if ec>255 then bad_font;
1899
 
read_tfm_word; tfm_b01(nw);
1900
 
if (nw=0)or(nw>256) then bad_font;
1901
 
for l:=-2 to lh do
1902
 
  begin read_tfm_word;
1903
 
  if l=1 then
1904
 
    begin tfm_squad(w); check_check_sum(w,true);
1905
 
    end
1906
 
  else if l=2 then
1907
 
    begin if tfm_b0>127 then bad_font;
1908
 
    check_design_size(round(tfm_conv*tfm_uquad));
1909
 
    end;
1910
 
  end
1911
 
 
1912
 
@ The width indices for the characters are stored in positions |n_chars|
1913
 
through |n_chars-bc+ec| of the |char_widths| array; if characters on
1914
 
either end of the range |bc..ec| do not exist, they are ignored and the
1915
 
range is adjusted accordingly.
1916
 
 
1917
 
@<TFM: Store character-width indices@>=
1918
 
read_tfm_word;
1919
 
while (tfm_b0=0)and(bc<=ec) do
1920
 
  begin incr(bc); read_tfm_word;
1921
 
  end;
1922
 
font_bc(cur_fnt):=bc; font_chars(cur_fnt):=n_chars-bc;
1923
 
if ec>=max_chars-font_chars(cur_fnt) then overflow(str_chars,max_chars);
1924
 
for l:=bc to ec do
1925
 
  begin char_widths[n_chars]:=tfm_b0; incr(n_chars); read_tfm_word;
1926
 
  end;
1927
 
while (char_widths[n_chars-1]=0)and(ec>=bc) do
1928
 
  begin decr(n_chars); decr(ec);
1929
 
  end;
1930
 
font_ec(cur_fnt):=ec
1931
 
 
1932
 
@ The most important part of |load_font| is the width computation, which
1933
 
involves multiplying the relative widths in the \.{TFM} file by the
1934
 
scaling factor in the \.{DVI} file. A similar computation is used for
1935
 
dimensions read from \.{VF} files. This fixed-point multiplication must
1936
 
be done with precisely the same accuracy by all \.{DVI}-reading programs,
1937
 
in order to validate the assumptions made by \.{DVI}-writing programs
1938
 
like \TeX82.
1939
 
 
1940
 
Let us therefore summarize what needs to be done. Each width in a \.{TFM}
1941
 
file appears as a four-byte quantity called a |fix_word|.  A |fix_word|
1942
 
whose respective bytes are $(a,b,c,d)$ represents the number
1943
 
$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
1944
 
b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
1945
 
-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
1946
 
(No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
1947
 
dimension must be less than 16.)  We want to multiply this quantity by the
1948
 
integer~|z|, which is known to be less than $2^{27}$.
1949
 
If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
1950
 
$d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
1951
 
16, to obtain a multiplier less than $2^{23}$, and we can compensate for
1952
 
this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
1953
 
$\beta=2^{4-e}$; we shall compute
1954
 
$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
1955
 
or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
1956
 
This calculation must be done exactly, for the reasons stated above; the
1957
 
following program does the job in a system-independent way, assuming
1958
 
that arithmetic is exact on numbers less than $2^{31}$ in magnitude. We
1959
 
use \.{WEB} macros for various versions of this computation.
1960
 
@^system dependencies@>
1961
 
@^optimization@>
1962
 
 
1963
 
@d tfm_fix3u== {convert |tfm_b1..tfm_b3| to an unsigned scaled dimension}
1964
 
(((((tfm_b3*z)div@'400)+(tfm_b2*z))div@'400)+(tfm_b1*z))div beta
1965
 
@#
1966
 
@d tfm_fix4(#)== {convert |tfm_b0..tfm_b3| to a scaled dimension}
1967
 
  #:=tfm_fix3u;
1968
 
  if tfm_b0>0 then if tfm_b0=255 then Decr(#)(alpha) else bad_font
1969
 
@d tfm_fix3(#)== {convert |tfm_b1..tfm_b3| to a scaled dimension}
1970
 
  #:=tfm_fix3u; @+ if tfm_b1>127 then Decr(#)(alpha)
1971
 
@d tfm_fix2== {convert |tfm_b2..tfm_b3| to a scaled dimension}
1972
 
  if tfm_b2>127 then tfm_b1:=255 else tfm_b1:=0;
1973
 
  tfm_fix3
1974
 
@d tfm_fix1== {convert |tfm_b3| to a scaled dimension}
1975
 
  if tfm_b3>127 then tfm_b1:=255 else tfm_b1:=0;
1976
 
  tfm_b2:=tfm_b1; tfm_fix3
1977
 
 
1978
 
@<Variables for scaling computation@>=
1979
 
@!z:int_32; {multiplier}
1980
 
@!alpha:int_32; {correction for negative values}
1981
 
@!beta:int_15; {divisor}
1982
 
 
1983
 
@ @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>=
1984
 
alpha:=16;
1985
 
while z>=@'40000000 do
1986
 
  begin z:=z div 2; alpha:=alpha+alpha;
1987
 
  end;
1988
 
beta:=256 div alpha; alpha:=alpha*z
1989
 
 
1990
 
@ The first width value, which indicates that a character does not exist
1991
 
and which must vanish, is converted to |invalid_width|; the other width
1992
 
values are scaled by |font_scaled(cur_fnt)| and converted to width
1993
 
pointers by |make_width|. The resulting width pointers are stored
1994
 
temporarily in the |char_widths| array, following the with indices.
1995
 
 
1996
 
@<TFM: Read and convert the width values@>=
1997
 
if nw-1>max_chars-n_chars then overflow(str_chars,max_chars);
1998
 
if (tfm_b0<>0)or(tfm_b1<>0)or(tfm_b2<>0)or(tfm_b3<>0) then bad_font
1999
 
  else char_widths[n_chars]:=invalid_width;
2000
 
z:=font_scaled(cur_fnt);
2001
 
@<Replace |z|...@>;
2002
 
for p:=n_chars+1 to n_chars+nw-1 do
2003
 
  begin read_tfm_word; tfm_fix4(w);
2004
 
  char_widths[p]:=make_width(w);
2005
 
  end
2006
 
 
2007
 
@ We simply translate the width indices into width pointers. In addition
2008
 
we initialize the character packets with the invalid packet.
2009
 
 
2010
 
@<TFM: Convert character-width indices to character-width pointers@>=
2011
 
for p:=font_chars(cur_fnt)+bc to n_chars-1 do
2012
 
  begin q:=char_widths[n_chars+char_widths[p]]; char_widths[p]:=q;
2013
 
  char_packets[p]:=invalid_packet;
2014
 
  end
2015
 
 
2016
 
@ When processing a font definition we put the data extracted from the
2017
 
\.{DVI} or \.{VF} file into position |nf| of the font data arrays and
2018
 
call |define_font| to obtain the internal font number for this font.
2019
 
The parameter |load| is true if the \.{TFM} file should be loaded.
2020
 
 
2021
 
@p function define_font(@!load:boolean):font_number;
2022
 
var save_fnt:font_number; {used to save |cur_fnt|}
2023
 
begin save_fnt:=cur_fnt; {save}
2024
 
cur_fnt:=0;
2025
 
while (font_name(cur_fnt)<>font_name(nf))or@|
2026
 
  (font_scaled(cur_fnt)<>font_scaled(nf)) do incr(cur_fnt);
2027
 
d_print(' => ',cur_fnt:1); print_font(cur_fnt);
2028
 
if cur_fnt<nf then
2029
 
  begin check_check_sum(font_check(nf),true);
2030
 
  check_design_size(font_design(nf));
2031
 
  @!debug if font_type(cur_fnt)=defined_font then print(' defined')
2032
 
  else print(' loaded');
2033
 
  print(' previously');
2034
 
  gubed@;
2035
 
  end
2036
 
else  begin if nf=max_fonts then overflow(str_fonts,max_fonts);
2037
 
  incr(nf); font_font(cur_fnt):=invalid_font;
2038
 
  font_type(cur_fnt):=defined_font;
2039
 
  d_print(' defined');
2040
 
  end;
2041
 
print_ln('.');
2042
 
if load and(font_type(cur_fnt)=defined_font) then load_font;
2043
 
define_font:=cur_fnt;
2044
 
cur_fnt:=save_fnt; {restore}
2045
 
end;
2046
 
 
2047
 
@* Low-level DVI input routines.
2048
 
The program uses the binary file variable |dvi_file| for its main input
2049
 
file; |dvi_loc| is the number of the byte about to be read next from
2050
 
|dvi_file|.
2051
 
 
2052
 
@<Glob...@>=
2053
 
@!dvi_file:byte_file; {the stuff we are \.{\title}ing}
2054
 
@!dvi_loc:int_32; {where we are about to look, in |dvi_file|}
2055
 
 
2056
 
@ If the \.{DVI} file is badly malformed, we say |bad_dvi|; this
2057
 
procedure gives an error message which refers the user to \.{DVItype},
2058
 
and terminates \.{\title}.
2059
 
 
2060
 
@<Error handling...@>=
2061
 
procedure bad_dvi;
2062
 
begin new_line; print_ln('Bad DVI file: loc=',dvi_loc:1,'!');
2063
 
@.Bad DVI file@>
2064
 
print(' Use DVItype with output level');
2065
 
@.Use DVItype@>
2066
 
if random_reading then print('=4') @+ else print('<4');
2067
 
abort('to diagnose the problem');
2068
 
end;
2069
 
 
2070
 
@ To prepare |dvi_file| for input, we |reset| it.
2071
 
 
2072
 
@<Open input file(s)@>=
2073
 
reset(dvi_file); {prepares to read packed bytes from |dvi_file|}
2074
 
dvi_loc:=0;
2075
 
 
2076
 
@ Reading the \.{DVI} file should be done as efficient as possible for a
2077
 
particular system; on many systems this means that a large number of
2078
 
bytes from |dvi_file| is read into a buffer and will then be extracted
2079
 
from that buffer. In order to simplify such system dependent changes
2080
 
we use a pair of \.{WEB} macros: |dvi_byte| extracts the next \.{DVI}
2081
 
byte and |dvi_eof| is |true| if we have reached the end of the \.{DVI}
2082
 
file. Here we give simple minded definitions for these macros in terms
2083
 
of standard \PASCAL.
2084
 
@^system dependencies@>
2085
 
@^optimization@>
2086
 
 
2087
 
@d dvi_eof == eof(dvi_file) {has the \.{DVI} file been exhausted?}
2088
 
@d dvi_byte(#) ==
2089
 
  if dvi_eof then bad_dvi
2090
 
  else read(dvi_file,#) {obtain next \.{DVI} byte}
2091
 
 
2092
 
@ Next we come to the routines that are used only if |random_reading|    is
2093
 
|true|. The driver program below needs two such routines: |dvi_length| should
2094
 
compute the total number of bytes in |dvi_file|, possibly also
2095
 
causing |eof(dvi_file)| to be true; and |dvi_move(n)| should position
2096
 
|dvi_file| so that the next |dvi_byte| will read byte |n|, starting with
2097
 
|n=0| for the first byte in the file.
2098
 
@^system dependencies@>
2099
 
 
2100
 
Such routines are, of course, highly system dependent. They are implemented
2101
 
here in terms of two assumed system routines called |set_pos| and |cur_pos|.
2102
 
The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is
2103
 
negative or larger than the total number of items in |f|; in the latter
2104
 
case, |set_pos(f,n)| moves to the end of file |f|.
2105
 
The call |cur_pos(f)| gives the total number of items in |f|, if
2106
 
|eof(f)| is true; we use |cur_pos| only in such a situation.
2107
 
 
2108
 
@p function dvi_length:int_32;
2109
 
begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file);
2110
 
end;
2111
 
@#
2112
 
procedure dvi_move(@!n:int_32);
2113
 
begin set_pos(dvi_file,n); dvi_loc:=n;
2114
 
end;
2115
 
 
2116
 
@ We need seven simple functions to read the next byte or bytes
2117
 
from |dvi_file|.
2118
 
 
2119
 
@p function dvi_sbyte:int_8; {returns the next byte, signed}
2120
 
@!begin_byte(dvi_byte); incr(dvi_loc); comp_sbyte(dvi_sbyte);
2121
 
end;
2122
 
@#
2123
 
function dvi_ubyte:int_8u; {returns the next byte, unsigned}
2124
 
@!begin_byte(dvi_byte); incr(dvi_loc); comp_ubyte(dvi_ubyte);
2125
 
end;
2126
 
@#
2127
 
function dvi_spair:int_16; {returns the next two bytes, signed}
2128
 
@!begin_pair(dvi_byte); Incr(dvi_loc)(2); comp_spair(dvi_spair);
2129
 
end;
2130
 
@#
2131
 
function dvi_upair:int_16u; {returns the next two bytes, unsigned}
2132
 
@!begin_pair(dvi_byte); Incr(dvi_loc)(2); comp_upair(dvi_upair);
2133
 
end;
2134
 
@#
2135
 
function dvi_strio:int_24; {returns the next three bytes, signed}
2136
 
@!begin_trio(dvi_byte); Incr(dvi_loc)(3); comp_strio(dvi_strio);
2137
 
end;
2138
 
@#
2139
 
function dvi_utrio:int_24u; {returns the next three bytes, unsigned}
2140
 
@!begin_trio(dvi_byte); Incr(dvi_loc)(3); comp_utrio(dvi_utrio);
2141
 
end;
2142
 
@#
2143
 
function dvi_squad:int_32; {returns the next four bytes, signed}
2144
 
@!begin_quad(dvi_byte); Incr(dvi_loc)(4); comp_squad(dvi_squad);
2145
 
end;
2146
 
 
2147
 
@ Three other functions are used in cases where a four byte integer
2148
 
(which is always signed) must have a non-negative value, a positive
2149
 
value, or is a pointer which must be either positive or |=-1|.
2150
 
 
2151
 
@p function dvi_uquad:int_31; {result must be non-negative}
2152
 
var x:int_32;
2153
 
begin x:=dvi_squad; if x<0 then bad_dvi
2154
 
else dvi_uquad:=x;
2155
 
end;
2156
 
@#
2157
 
function dvi_pquad:int_31; {result must be positive}
2158
 
var x:int_32;
2159
 
begin x:=dvi_squad; if x<=0 then bad_dvi
2160
 
else dvi_pquad:=x;
2161
 
end;
2162
 
@#
2163
 
function dvi_pointer:int_32; {result must be positive or |=-1|}
2164
 
var x:int_32;
2165
 
begin x:=dvi_squad; if (x<=0)and(x<>-1) then bad_dvi
2166
 
else dvi_pointer:=x;
2167
 
end;
2168
 
 
2169
 
@ Given the structure of the \.{DVI} commands it is fairly obvious
2170
 
that their interpretation consists of two steps: First zero to four
2171
 
bytes are read in order to obtain the value of the first parameter
2172
 
(e.g., zero bytes for |set_char_0|, four bytes for |set4|); then,
2173
 
depending on the command class, a specific action is performed (e.g.,
2174
 
typeset a character but don't move the reference point for |put1..put4|).
2175
 
 
2176
 
The \.{DVItype} program uses large case statements for both steps;
2177
 
unfortunately some \PASCAL\ compilers fail to implement large case
2178
 
statements efficiently -- in particular those as the one used in the
2179
 
|first_par| function of \.{DVItype}. Here we use a pair of look up tables:
2180
 
|dvi_par| determines how to obtain the value of the first parameter, and
2181
 
|dvi_cl| determines the command class.
2182
 
 
2183
 
A slight complication arises from the fact that we want to decompose the
2184
 
character code of each character to be typset into a residue
2185
 
|0<=char_res<256| and extension: |char_code=char_res+256*char_ext|;
2186
 
the \.{TFM} widths as well as the pixel widths for a given resolution
2187
 
are the same for all characters in a font with the same residue.
2188
 
 
2189
 
@d two_cases(#)==#,#+1
2190
 
@d three_cases(#)==#,#+1,#+2
2191
 
@d five_cases(#)==#,#+1,#+2,#+3,#+4
2192
 
 
2193
 
@ First we define the values used as array elements of |dvi_par|; we
2194
 
distinguish between pure numbers and dimensions because dimensions read
2195
 
from a \.{VF} file must be scaled.
2196
 
 
2197
 
@d char_par=0 {character for \\{set} and |put|}
2198
 
@d no_par=1 {no parameter}
2199
 
@d dim1_par=2 {one-byte signed dimension}
2200
 
@d num1_par=3 {one-byte unsigned number}
2201
 
@d dim2_par=4 {two-byte signed dimension}
2202
 
@d num2_par=5 {two-byte unsigned number}
2203
 
@d dim3_par=6 {three-byte signed dimension}
2204
 
@d num3_par=7 {three-byte unsigned number}
2205
 
@d dim4_par=8 {four-byte signed dimension}
2206
 
@d num4_par=9 {four-byte signed number}
2207
 
@d numu_par=10 {four-byte non-negative number}
2208
 
@d rule_par=11 {dimensions for |set_rule| and |put_rule|}
2209
 
@d fnt_par=12 {font for |fnt_num| commands}
2210
 
@d max_par=12 {largest possible value}
2211
 
 
2212
 
@<Types...@>=
2213
 
@!cmd_par=char_par..max_par;
2214
 
 
2215
 
@ Here we declare the array |dvi_par|.
2216
 
 
2217
 
@<Globals...@>=
2218
 
@!dvi_par:packed array [eight_bits] of cmd_par;
2219
 
 
2220
 
@ And here we initialize it.
2221
 
 
2222
 
@<Set init...@>=
2223
 
for i:=0 to put1+3 do dvi_par[i]:=char_par;@/
2224
 
for i:=nop to 255 do dvi_par[i]:=no_par;@/
2225
 
dvi_par[set_rule]:=rule_par; dvi_par[put_rule]:=rule_par;@/
2226
 
dvi_par[right1]:=dim1_par; dvi_par[right1+1]:=dim2_par;
2227
 
dvi_par[right1+2]:=dim3_par; dvi_par[right1+3]:=dim4_par;@/
2228
 
for i:=fnt_num_0 to fnt_num_0+63 do dvi_par[i]:=fnt_par;@/
2229
 
dvi_par[fnt1]:=num1_par; dvi_par[fnt1+1]:=num2_par;
2230
 
dvi_par[fnt1+2]:=num3_par; dvi_par[fnt1+3]:=num4_par;@/
2231
 
dvi_par[xxx1]:=num1_par; dvi_par[xxx1+1]:=num2_par;
2232
 
dvi_par[xxx1+2]:=num3_par; dvi_par[xxx1+3]:=numu_par;@/
2233
 
for i:=0 to 3 do
2234
 
  begin dvi_par[i+w1]:=dvi_par[i+right1];
2235
 
  dvi_par[i+x1]:=dvi_par[i+right1];
2236
 
  dvi_par[i+down1]:=dvi_par[i+right1];
2237
 
  dvi_par[i+y1]:=dvi_par[i+right1];
2238
 
  dvi_par[i+z1]:=dvi_par[i+right1];
2239
 
  dvi_par[i+fnt_def1]:=dvi_par[i+fnt1];
2240
 
  end;
2241
 
 
2242
 
@ Next we define the values used as array elements of |dvi_cl|;
2243
 
several \.{DVI} commands (e.g., |nop|, |bop|, |eop|, |pre|, |post|) will
2244
 
always be treated separately and are therfore assigned to the invalid
2245
 
class here.
2246
 
 
2247
 
@d char_cl=0
2248
 
@d rule_cl=char_cl+1
2249
 
@d xxx_cl=char_cl+2
2250
 
@d push_cl=3
2251
 
@d pop_cl=4
2252
 
@d w0_cl=5
2253
 
@d x0_cl=w0_cl+1
2254
 
@d right_cl=w0_cl+2
2255
 
@d w_cl=w0_cl+3
2256
 
@d x_cl=w0_cl+4
2257
 
@d y0_cl=10
2258
 
@d z0_cl=y0_cl+1
2259
 
@d down_cl=y0_cl+2
2260
 
@d y_cl=y0_cl+3
2261
 
@d z_cl=y0_cl+4
2262
 
@d fnt_cl=15
2263
 
@d fnt_def_cl=16
2264
 
@d invalid_cl=17
2265
 
@d max_cl=invalid_cl {largest possible value}
2266
 
 
2267
 
@<Types...@>=
2268
 
@!cmd_cl=char_cl..max_cl;
2269
 
 
2270
 
@ Here we declare the array |dvi_cl|.
2271
 
 
2272
 
@<Globals...@>=
2273
 
@!dvi_cl:packed array [eight_bits] of cmd_cl;
2274
 
 
2275
 
@ And here we initialize it.
2276
 
 
2277
 
@<Set init...@>=
2278
 
for i:=set_char_0 to put1+3 do dvi_cl[i]:=char_cl;
2279
 
dvi_cl[set_rule]:=rule_cl; dvi_cl[put_rule]:=rule_cl;@/
2280
 
dvi_cl[nop]:=invalid_cl;
2281
 
dvi_cl[bop]:=invalid_cl; dvi_cl[eop]:=invalid_cl;@/
2282
 
dvi_cl[push]:=push_cl; dvi_cl[pop]:=pop_cl;@/
2283
 
dvi_cl[w0]:=w0_cl; dvi_cl[x0]:=x0_cl;@/
2284
 
dvi_cl[y0]:=y0_cl; dvi_cl[z0]:=z0_cl;@/
2285
 
for i:=0 to 3 do
2286
 
  begin dvi_cl[i+right1]:=right_cl;
2287
 
  dvi_cl[i+w1]:=w_cl;
2288
 
  dvi_cl[i+x1]:=x_cl;@/
2289
 
  dvi_cl[i+down1]:=down_cl;
2290
 
  dvi_cl[i+y1]:=y_cl;
2291
 
  dvi_cl[i+z1]:=z_cl;@/
2292
 
  dvi_cl[i+xxx1]:=xxx_cl;
2293
 
  dvi_cl[i+fnt_def1]:=fnt_def_cl;
2294
 
  end;
2295
 
for i:=fnt_num_0 to fnt1+3 do dvi_cl[i]:=fnt_cl;
2296
 
for i:=pre to 255 do dvi_cl[i]:=invalid_cl;
2297
 
 
2298
 
@ A few small arrays are used to generate \.{DVI} commands.
2299
 
 
2300
 
@<Glob...@>=
2301
 
@!dvi_char_cmd:array[boolean] of eight_bits; {|put1| and |set1|}
2302
 
@!dvi_rule_cmd:array[boolean] of eight_bits; {|put_rule| and |set_rule|}
2303
 
@!dvi_right_cmd:array[right_cl..x_cl] of eight_bits; {|right1|, |w1|, and |x1|}
2304
 
@!dvi_down_cmd:array[down_cl..z_cl] of eight_bits; {|down1|, |y1|, and |z1|}
2305
 
 
2306
 
@ @<Set init...@>=
2307
 
dvi_char_cmd[false]:=put1;
2308
 
dvi_char_cmd[true]:=set1;@/
2309
 
dvi_rule_cmd[false]:=put_rule;
2310
 
dvi_rule_cmd[true]:=set_rule;@/
2311
 
dvi_right_cmd[right_cl]:=right1;
2312
 
dvi_right_cmd[w_cl]:=w1;
2313
 
dvi_right_cmd[x_cl]:=x1;@/
2314
 
dvi_down_cmd[down_cl]:=down1;
2315
 
dvi_down_cmd[y_cl]:=y1;
2316
 
dvi_down_cmd[z_cl]:=z1;
2317
 
 
2318
 
@ The global variables |cur_cmd|, |cur_parm| and |cur_class| are used
2319
 
for the current \.{DVI} command, its first parameter (if any), and its
2320
 
command class respectively.
2321
 
 
2322
 
@<Glob...@>=
2323
 
@!cur_cmd:eight_bits; {current \.{DVI} command byte}
2324
 
@!cur_parm:int_32; {its first parameter (if any)}
2325
 
@!cur_class:cmd_cl; {its class}
2326
 
 
2327
 
@ When typesetting a character or rule, the boolean variable |cur_upd|
2328
 
is |true| for \\{set} commands, |false| for |put| commands.
2329
 
 
2330
 
@<Glob...@>=
2331
 
@!cur_cp:char_pointer; {|char_widths| index for the current character}
2332
 
@!cur_wp:width_pointer; {width pointer of the current character}
2333
 
@!cur_upd:boolean; {is this a \\{set} or |set_rule| command ?}
2334
 
@!cur_v_dimen:int_32; {a vertical dimension}
2335
 
@!cur_h_dimen:int_32; {a horizontal dimension}
2336
 
 
2337
 
@ @<Set init...@>=
2338
 
cur_cp:=0; cur_wp:=invalid_width; {so they can be saved and restored!}
2339
 
 
2340
 
@ The |dvi_first_par| procedure first reads \.{DVI} command bytes into
2341
 
|cur_cmd| until |cur_cmd<>nop|; then |cur_parm| is set to the value of
2342
 
the first parameter (if any) and |cur_class| to the command class.
2343
 
 
2344
 
@d set_cur_char(#)== {set up |cur_res|, |cur_ext|, and |cur_upd|}
2345
 
begin cur_ext:=0;
2346
 
if cur_cmd<set1 then
2347
 
  begin cur_res:=cur_cmd; cur_upd:=true
2348
 
  end
2349
 
else  begin cur_res:=#; cur_upd:=(cur_cmd<put1);
2350
 
  Decr(cur_cmd)(dvi_char_cmd[cur_upd]);
2351
 
  while cur_cmd>0 do
2352
 
    begin if cur_cmd=3 then if cur_res>127 then cur_ext:=-1;
2353
 
    cur_ext:=cur_ext*256+cur_res; cur_res:=#; decr(cur_cmd);
2354
 
    end;
2355
 
  end;
2356
 
end
2357
 
 
2358
 
@p procedure dvi_first_par;
2359
 
begin repeat cur_cmd:=dvi_ubyte;
2360
 
until cur_cmd<>nop; {skip over |nop|s}
2361
 
case dvi_par[cur_cmd] of
2362
 
char_par: set_cur_char(dvi_ubyte);
2363
 
no_par: do_nothing;
2364
 
dim1_par: cur_parm:=dvi_sbyte;
2365
 
num1_par: cur_parm:=dvi_ubyte;
2366
 
dim2_par: cur_parm:=dvi_spair;
2367
 
num2_par: cur_parm:=dvi_upair;
2368
 
dim3_par: cur_parm:=dvi_strio;
2369
 
num3_par: cur_parm:=dvi_utrio;
2370
 
two_cases(dim4_par): cur_parm:=dvi_squad; {|dim4_par| and |num4_par|}
2371
 
numu_par: cur_parm:=dvi_uquad;
2372
 
rule_par:
2373
 
  begin cur_v_dimen:=dvi_squad; cur_h_dimen:=dvi_squad;
2374
 
  cur_upd:=(cur_cmd=set_rule);
2375
 
  end;
2376
 
fnt_par:cur_parm:=cur_cmd-fnt_num_0;
2377
 
end; {there are no other cases}
2378
 
cur_class:=dvi_cl[cur_cmd];
2379
 
end;
2380
 
 
2381
 
@ The global variable |dvi_nf| is used for the number of different
2382
 
\.{DVI} fonts defined so far; their external font numbers (as extracted
2383
 
from the \.{DVI} file) are stored in the array |dvi_e_fnts|, the
2384
 
corresponding internal font numbers used internally by \.{\title} are
2385
 
stored in the array |dvi_i_fnts|.
2386
 
 
2387
 
@<Glob...@>=
2388
 
@!dvi_e_fnts:array[font_number] of int_32; {external font numbers}
2389
 
@!dvi_i_fnts:array[font_number] of font_number; {corresponding
2390
 
  internal font numbers}
2391
 
@!dvi_nf:font_number; {number of \.{DVI} fonts defined so far}
2392
 
 
2393
 
@ @<Set ini...@>=
2394
 
dvi_nf:=0;
2395
 
 
2396
 
@ The |dvi_font| procedure sets |cur_fnt| to the internal font number
2397
 
corresponding to the external font number |cur_parm| (or aborts the
2398
 
program if such a font was never defined).
2399
 
 
2400
 
@p procedure dvi_font; {computes |cur_fnt| corresponding to |cur_parm|}
2401
 
var f:font_number; {where the font is sought}
2402
 
begin @<DVI: Locate font |cur_parm|@>;
2403
 
if f=dvi_nf then bad_dvi;
2404
 
cur_fnt:=dvi_i_fnts[f];
2405
 
if font_type(cur_fnt)=defined_font then load_font;
2406
 
end;
2407
 
 
2408
 
@ @<DVI: Locate font |cur_parm|@>=
2409
 
f:=0; dvi_e_fnts[dvi_nf]:=cur_parm;
2410
 
while cur_parm<>dvi_e_fnts[f] do incr(f)
2411
 
 
2412
 
@ Finally the |dvi_do_font| procedure is called when one of the command
2413
 
|fnt_def1..fnt_def4| and its first parameter have been read from the
2414
 
\.{DVI} file; the argument indicates whether this should be the second
2415
 
definition of the font (|true|) or not (|false|).
2416
 
 
2417
 
@p procedure dvi_do_font(@!second:boolean);
2418
 
var f:font_number; {where the font is sought}
2419
 
@!k:int_15; {general purpose variable}
2420
 
begin print('DVI: font ',cur_parm:1);
2421
 
@<DVI: Locate font |cur_parm|@>;
2422
 
if (f=dvi_nf)=second then bad_dvi;
2423
 
font_check(nf):=dvi_squad;
2424
 
font_scaled(nf):=dvi_pquad;
2425
 
font_design(nf):=dvi_pquad;
2426
 
k:=dvi_ubyte; pckt_room(1); append_byte(k);
2427
 
Incr(k)(dvi_ubyte); pckt_room(k);
2428
 
while k>0 do  begin append_byte(dvi_ubyte); decr(k);
2429
 
  end;
2430
 
font_name(nf):=make_packet; {the font area plus name}
2431
 
dvi_i_fnts[dvi_nf]:=define_font(false);
2432
 
if not second then
2433
 
  begin if dvi_nf=max_fonts then overflow(str_fonts,max_fonts);
2434
 
  incr(dvi_nf);
2435
 
  end
2436
 
else if dvi_i_fnts[f]<>dvi_i_fnts[dvi_nf] then bad_dvi;
2437
 
end;
2438
 
 
2439
 
@* Low-level VF input routines.
2440
 
A detailed description of the \.{VF} file format can be found in the
2441
 
documentation of \.{VFtoVP}; here we just define symbolic names for
2442
 
some of the \.{VF} command bytes.
2443
 
 
2444
 
@d long_char=242 {\.{VF} command for general character packet}
2445
 
@#
2446
 
@d vf_id=202 {identifies \.{VF} files}
2447
 
 
2448
 
@ The program uses the binary file variable |vf_file| for input from
2449
 
\.{VF} files; |vf_loc| is the number of the byte about to be read next
2450
 
from |vf_file|.
2451
 
 
2452
 
@<Glob...@>=
2453
 
@!vf_file:byte_file; {a \.{VF} file}
2454
 
@!vf_loc:int_32; {where we are about to look, in |vf_file|}
2455
 
@!vf_limit:int_32; {value of |vf_loc| at end of a character packet}
2456
 
@!vf_ext:pckt_pointer; {extension for \.{VF} files}
2457
 
@!vf_cur_fnt:font_number; {current font number in a \.{VF} file}
2458
 
 
2459
 
@ @<Initialize predefined strings@>=
2460
 
id3(".")("V")("F")(vf_ext); {file name extension for \.{VF} files}
2461
 
 
2462
 
@ If a \.{VF} file is badly malformed, we say |bad_font|; this procedure
2463
 
gives an error message which refers the user to \.{VFtoVP} and \.{VPtoVF},
2464
 
and terminates \.{\title}.
2465
 
 
2466
 
@<Cases for |bad_font|@>=
2467
 
vf_font_type: begin print('Bad VF file'); print_font(cur_fnt);
2468
 
@.Bad VF file@>
2469
 
  print_ln(' loc=',vf_loc:1);
2470
 
  abort('Use VFtoVP/VPtoVF to diagnose and correct the problem');
2471
 
@.Use VFtoVP/VPtoVF@>
2472
 
  end;
2473
 
 
2474
 
@ If no font directory has been specified, \.{\title} is supposed to use
2475
 
the default \.{VF} directory, which is a system-dependent place where
2476
 
the \.{VF} files for standard fonts are kept.
2477
 
The string variable |VF_default_area| contains the name of this area.
2478
 
@^system dependencies@>
2479
 
 
2480
 
@d VF_default_area_name=='TeXvfonts:' {change this to the correct name}
2481
 
@d VF_default_area_name_length=10 {change this to the correct length}
2482
 
 
2483
 
@<Glob...@>=
2484
 
@!VF_default_area:packed array[1..VF_default_area_name_length] of char;
2485
 
 
2486
 
@ @<Set init...@>=
2487
 
VF_default_area:=VF_default_area_name;
2488
 
 
2489
 
@ To prepare |vf_file| for input we |reset| it.
2490
 
 
2491
 
@<VF: Open |vf_file| or |goto not_found|@>=
2492
 
make_font_name(VF_default_area_name_length)(VF_default_area)(vf_ext);
2493
 
reset(vf_file,cur_name);
2494
 
if eof(vf_file) then
2495
 
@^system dependencies@>
2496
 
  goto not_found;
2497
 
vf_loc:=0
2498
 
 
2499
 
@ Reading a \.{VF} file should be done as efficient as possible for a
2500
 
particular system; on many systems this means that a large number of
2501
 
bytes from |vf_file| is read into a buffer and will then be extracted
2502
 
from that buffer. In order to simplify such system dependent changes
2503
 
we use a pair of \.{WEB} macros: |vf_byte| extracts the next \.{VF}
2504
 
byte and |vf_eof| is |true| if we have reached the end of the \.{VF}
2505
 
file. Here we give simple minded definitions for these macros in terms
2506
 
of standard \PASCAL.
2507
 
@^system dependencies@>
2508
 
@^optimization@>
2509
 
 
2510
 
@d vf_eof == eof(vf_file) {has the \.{VF} file been exhausted?}
2511
 
@d vf_byte(#) ==
2512
 
  if vf_eof then bad_font
2513
 
  else read(vf_file,#) {obtain next \.{VF} byte}
2514
 
 
2515
 
@ We need several simple functions to read the next byte or bytes
2516
 
from |vf_file|.
2517
 
 
2518
 
@p function vf_ubyte:int_8u; {returns the next byte, unsigned}
2519
 
@!begin_byte(vf_byte); incr(vf_loc); comp_ubyte(vf_ubyte);
2520
 
end;
2521
 
@#
2522
 
function vf_upair:int_16u; {returns the next two bytes, unsigned}
2523
 
@!begin_pair(vf_byte); Incr(vf_loc)(2); comp_upair(vf_upair);
2524
 
end;
2525
 
@#
2526
 
function vf_strio:int_24; {returns the next three bytes, signed}
2527
 
@!begin_trio(vf_byte); Incr(vf_loc)(3); comp_strio(vf_strio);
2528
 
end;
2529
 
@#
2530
 
function vf_utrio:int_24u; {returns the next three bytes, unsigned}
2531
 
@!begin_trio(vf_byte); Incr(vf_loc)(3); comp_utrio(vf_utrio);
2532
 
end;
2533
 
@#
2534
 
function vf_squad:int_32; {returns the next four bytes, signed}
2535
 
@!begin_quad(vf_byte); Incr(vf_loc)(4); comp_squad(vf_squad);
2536
 
end;
2537
 
 
2538
 
@ All dimensions in a \.{VF} file, except the design sizes of a virtual
2539
 
font and its local fonts, are |fix_word|s that must be scaled in exactly
2540
 
the same way as the character widths from a \.{TFM} file; we can use the
2541
 
same code, but this time |z|, |alpha|, and |beta| are global variables.
2542
 
 
2543
 
@<Glob...@>=
2544
 
@<Variables for scaling computation@>@;
2545
 
 
2546
 
@ We need five functions to read the next byte or bytes and convert a
2547
 
|fix_word| to a scaled dimension.
2548
 
 
2549
 
@p function vf_fix1:int_32; {returns the next byte as scaled value}
2550
 
var x:int_32; {accumulator}
2551
 
begin vf_byte(tfm_b3); incr(vf_loc);
2552
 
tfm_fix1(x); vf_fix1:=x;
2553
 
end;
2554
 
@#
2555
 
function vf_fix2:int_32; {returns the next two bytes as scaled value}
2556
 
var x:int_32; {accumulator}
2557
 
begin vf_byte(tfm_b2); vf_byte(tfm_b3); Incr(vf_loc)(2);
2558
 
tfm_fix2(x); vf_fix2:=x;
2559
 
end;
2560
 
@#
2561
 
function vf_fix3:int_32; {returns the next three bytes as scaled value}
2562
 
var x:int_32; {accumulator}
2563
 
begin vf_byte(tfm_b1); vf_byte(tfm_b2); vf_byte(tfm_b3);
2564
 
Incr(vf_loc)(3);@/
2565
 
tfm_fix3(x); vf_fix3:=x;
2566
 
end;
2567
 
@#
2568
 
function vf_fix3u:int_32; {returns the next three bytes as scaled value}
2569
 
begin vf_byte(tfm_b1); vf_byte(tfm_b2); vf_byte(tfm_b3);
2570
 
Incr(vf_loc)(3);@/
2571
 
vf_fix3u:=tfm_fix3u;
2572
 
end;
2573
 
@#
2574
 
function vf_fix4:int_32; {returns the next four bytes as scaled value}
2575
 
var x:int_32; {accumulator}
2576
 
begin vf_byte(tfm_b0); vf_byte(tfm_b1); vf_byte(tfm_b2); vf_byte(tfm_b3);
2577
 
Incr(vf_loc)(4);@/
2578
 
tfm_fix4(x); vf_fix4:=x;
2579
 
end;
2580
 
 
2581
 
@ Three other functions are used in cases where the result must have a
2582
 
non-negative value or a positive value.
2583
 
 
2584
 
@p function vf_uquad:int_31; {result must be non-negative}
2585
 
var x:int_32;
2586
 
begin x:=vf_squad; if x<0 then bad_font @+ else vf_uquad:=x;
2587
 
end;
2588
 
@#
2589
 
function vf_pquad:int_31; {result must be positive}
2590
 
var x:int_32;
2591
 
begin x:=vf_squad; if x<=0 then bad_font @+ else vf_pquad:=x;
2592
 
end;
2593
 
@#
2594
 
function vf_fixp:int_31; {result must be positive}
2595
 
var x:int_32; {accumulator}
2596
 
begin vf_byte(tfm_b0); vf_byte(tfm_b1); vf_byte(tfm_b2); vf_byte(tfm_b3);
2597
 
Incr(vf_loc)(4);@/
2598
 
if tfm_b0>0 then bad_font;
2599
 
vf_fixp:=tfm_fix3u;
2600
 
end;
2601
 
 
2602
 
@ The |vf_first_par| procedure first reads a \.{VF} command byte into
2603
 
|cur_cmd|; then |cur_parm| is set to the value of the first parameter
2604
 
(if any) and |cur_class| to the command class.
2605
 
 
2606
 
@d set_cur_wp_end(#)== if cur_wp=invalid_width then #
2607
 
@d set_cur_wp(#)== {set |cur_wp| to the char's width pointer}
2608
 
cur_wp:=invalid_width;
2609
 
if #<>invalid_font then
2610
 
  if (cur_res>=font_bc(#))and(cur_res<=font_ec(#)) then
2611
 
    begin cur_cp:=font_chars(#)+cur_res; cur_wp:=char_widths[cur_cp];
2612
 
    end;
2613
 
set_cur_wp_end
2614
 
 
2615
 
@p procedure vf_first_par;
2616
 
begin cur_cmd:=vf_ubyte;
2617
 
case dvi_par[cur_cmd] of
2618
 
char_par:
2619
 
  begin set_cur_char(vf_ubyte); set_cur_wp(vf_cur_fnt)(bad_font);
2620
 
  end;
2621
 
no_par: do_nothing;
2622
 
dim1_par: cur_parm:=vf_fix1;
2623
 
num1_par: cur_parm:=vf_ubyte;
2624
 
dim2_par: cur_parm:=vf_fix2;
2625
 
num2_par: cur_parm:=vf_upair;
2626
 
dim3_par: cur_parm:=vf_fix3;
2627
 
num3_par: cur_parm:=vf_utrio;
2628
 
dim4_par: cur_parm:=vf_fix4;
2629
 
num4_par: cur_parm:=vf_squad;
2630
 
numu_par: cur_parm:=vf_uquad;
2631
 
rule_par:
2632
 
  begin cur_v_dimen:=vf_fix4; cur_h_dimen:=vf_fix4;
2633
 
  cur_upd:=(cur_cmd=set_rule);
2634
 
  end;
2635
 
fnt_par:cur_parm:=cur_cmd-fnt_num_0;
2636
 
end; {there are no other cases}
2637
 
cur_class:=dvi_cl[cur_cmd];
2638
 
end;
2639
 
 
2640
 
@ For a virtual font we set |font_type(f):=vf_font_type|; in this case
2641
 
|font_font(f)| is the default font for character packets from virtual
2642
 
font~|f|.
2643
 
@^font types@>
2644
 
 
2645
 
The global variable |vf_nf| is used for the number of different local
2646
 
fonts defined in a \.{VF} file so far; their external font numbers (as
2647
 
extracted from the \.{VF} file) are stored in the array |vf_e_fnts|, the
2648
 
corresponding internal font numbers used internally by \.{\title} are
2649
 
stored in the array |vf_i_fnts|.
2650
 
 
2651
 
@<Glob...@>=
2652
 
@!vf_e_fnts:array[font_number] of int_32; {external font numbers}
2653
 
@!vf_i_fnts:array[font_number] of font_number; {corresponding
2654
 
  internal font numbers}
2655
 
@!vf_nf:font_number; {number of local fonts defined so far}
2656
 
@!lcl_nf:font_number; {largest |vf_nf| value for any \.{VF} file}
2657
 
 
2658
 
@ @<Set init...@>=
2659
 
lcl_nf:=0;
2660
 
 
2661
 
@ The |vf_font| procedure sets |vf_cur_fnt| to the internal font number
2662
 
corresponding to the external font number |cur_parm| (or aborts the
2663
 
program if such a font was never defined).
2664
 
 
2665
 
@p procedure vf_font; {computes |vf_cur_fnt| corresponding to |cur_parm|}
2666
 
var f:font_number; {where the font is sought}
2667
 
begin @<VF: Locate font |cur_parm|@>;
2668
 
if f=vf_nf then bad_font;
2669
 
vf_cur_fnt:=vf_i_fnts[f];
2670
 
end;
2671
 
 
2672
 
@ @<VF: Locate font |cur_parm|@>=
2673
 
f:=0; vf_e_fnts[vf_nf]:=cur_parm;
2674
 
while cur_parm<>vf_e_fnts[f] do incr(f)
2675
 
 
2676
 
@ Finally the |vf_do_font| procedure is called when one of the command
2677
 
|fnt_def1..fnt_def4| and its first parameter have been read from the
2678
 
\.{VF} file.
2679
 
 
2680
 
@p procedure vf_do_font;
2681
 
var f:font_number; {where the font is sought}
2682
 
@!k:int_15; {general purpose variable}
2683
 
begin print('VF: font ',cur_parm:1);@/
2684
 
@<VF: Locate font |cur_parm|@>;
2685
 
if f<>vf_nf then bad_font;
2686
 
font_check(nf):=vf_squad;
2687
 
font_scaled(nf):=vf_fixp;
2688
 
font_design(nf):=round(tfm_conv*vf_pquad);
2689
 
k:=vf_ubyte; pckt_room(1); append_byte(k);
2690
 
Incr(k)(vf_ubyte); pckt_room(k);
2691
 
while k>0 do  begin append_byte(vf_ubyte); decr(k);
2692
 
  end;
2693
 
font_name(nf):=make_packet; {the font area plus name}
2694
 
vf_i_fnts[vf_nf]:=define_font(true);
2695
 
if vf_nf=lcl_nf then
2696
 
  if lcl_nf=max_fonts then overflow(str_fonts,max_fonts)
2697
 
  else incr(lcl_nf);
2698
 
incr(vf_nf);
2699
 
end;
2700
 
 
2701
 
@* Reading VF files.
2702
 
The |do_vf| function attempts to read the \.{VF} file for a font and
2703
 
returns |false| if the \.{VF} file could not be found; otherwise the
2704
 
font type is changed to |vf_font_type|.
2705
 
 
2706
 
@p function do_vf:boolean; {read a \.{VF} file}
2707
 
label reswitch,done,not_found,exit;
2708
 
var temp_int:int_32; {integer for temporary variables}
2709
 
@!temp_byte:int_8u; {byte for temporary variables}
2710
 
@!k:byte_pointer; {index into |byte_mem|}
2711
 
@!l:int_15; {general purpose variable}
2712
 
@!save_ext:int_24; {used to save |cur_ext|}
2713
 
@!save_res:int_8u; {used to save |cur_res|}
2714
 
@!save_cp:width_pointer; {used to save |cur_cp|}
2715
 
@!save_wp:width_pointer; {used to save |cur_wp|}
2716
 
@!save_upd:boolean; {used to save |cur_upd|}
2717
 
@!vf_wp:width_pointer; {width pointer for the current character packet}
2718
 
@!vf_fnt:font_number; {current font in the current character packet}
2719
 
@!move_zero:boolean; {|true| if rule 1 is used}
2720
 
@!last_pop:boolean; {|true| if final |pop| has been manufactured}
2721
 
begin @<VF: Open |vf_file| or |goto not_found|@>;
2722
 
save_ext:=cur_ext; save_res:=cur_res; save_cp:=cur_cp; save_wp:=cur_wp;
2723
 
save_upd:=cur_upd; {save}
2724
 
font_type(cur_fnt):=vf_font_type;@/
2725
 
@<VF: Process the preamble@>;@/
2726
 
@<VF: Process the font definitions@>;@/
2727
 
while cur_cmd<=long_char do @<VF: Build a character packet@>;
2728
 
if cur_cmd<>post then bad_font;
2729
 
@!debug print('VF file for font ',cur_fnt:1); print_font(cur_fnt);
2730
 
print_ln(' loaded.');
2731
 
gubed @;@/
2732
 
close_in(vf_file);
2733
 
cur_ext:=save_ext; cur_res:=save_res; cur_cp:=save_cp; cur_wp:=save_wp;
2734
 
cur_upd:=save_upd; {restore}
2735
 
do_vf:=true; return;
2736
 
not_found:do_vf:=false;
2737
 
exit:end;
2738
 
 
2739
 
@ @<VF: Process the preamble@>=
2740
 
if vf_ubyte<>pre then bad_font;
2741
 
if vf_ubyte<>vf_id then bad_font;
2742
 
temp_byte:=vf_ubyte; pckt_room(temp_byte);
2743
 
for l:=1 to temp_byte do append_byte(vf_ubyte);
2744
 
print('VF file: '''); print_packet(new_packet); print(''',');
2745
 
flush_packet;@/
2746
 
check_check_sum(vf_squad,false);
2747
 
check_design_size(round(tfm_conv*vf_pquad));@/
2748
 
z:=font_scaled(cur_fnt);
2749
 
@<Replace |z|...@>;@/
2750
 
print_nl('   for font ',cur_fnt:1); print_font(cur_fnt); print_ln('.')
2751
 
 
2752
 
@ @<VF: Process the font definitions@>=
2753
 
vf_i_fnts[0]:=invalid_font; vf_nf:=0;@/
2754
 
cur_cmd:=vf_ubyte;
2755
 
while (cur_cmd>=fnt_def1)and(cur_cmd<=fnt_def1+3) do
2756
 
  begin case cur_cmd-fnt_def1 of
2757
 
  0: cur_parm:=vf_ubyte;
2758
 
  1: cur_parm:=vf_upair;
2759
 
  2: cur_parm:=vf_utrio;
2760
 
  3: cur_parm:=vf_squad;
2761
 
  end; {there are no other cases}
2762
 
  vf_do_font;
2763
 
  cur_cmd:=vf_ubyte;
2764
 
  end;
2765
 
font_font(cur_fnt):=vf_i_fnts[0]
2766
 
 
2767
 
@ The \.{VF} format specifies that the interpretation of each packet
2768
 
begins with |w=x=y=z=0|; any |w0|, |x0|, |y0|, or |z0| command using
2769
 
these initial values will be ignored.
2770
 
 
2771
 
@<Types...@>=
2772
 
@!vf_state=array[0..1,0..1] of boolean; {state of |w|, |x|, |y|, and |z|}
2773
 
 
2774
 
@ As implied by the \.{VF} format the \.{DVI} commands read from the
2775
 
\.{VF} file are enclosed by |push| and |pop|; as we read \.{DVI}
2776
 
commands and append them to |byte_mem|, we perform a set of
2777
 
transformations in order to simplify the resulting packet: Let |zero| be
2778
 
any of the commands |put|, |put_rule|, |fnt_num|, |fnt|, or |xxx| which
2779
 
all leave the current position on the page unchanged, let |move| be any
2780
 
of the horizontal or vertical movement commands |right1..z4|, and let
2781
 
|any| be any sequence of commands containing |push| and |pop| in
2782
 
properly nested pairs; whenever possible we apply one of the following
2783
 
transformation rules: $$\def\n#1:{\hbox to 3cm{\hfil#1:}}
2784
 
\leqalignno{
2785
 
\hbox{|push| |zero|}&\RA\hbox{|zero| |push|}&\n1:\cr
2786
 
\hbox{|move| |pop|}&\RA\hbox{|pop|}&\n2:\cr
2787
 
\hbox{|push| |pop|}&\RA{}&\n3:\cr
2788
 
\hbox{|push| |set_char| |pop|}&\RA\hbox{|put|}&\n4a:\cr
2789
 
\hbox{|push| \\{set} |pop|}&\RA\hbox{|put|}&\n4b:\cr
2790
 
\hbox{|push| |set_rule| |pop|}&\RA\hbox{|put_rule|}&\n4c:\cr
2791
 
\hbox{|push| |push| |any| |pop|}&\RA\hbox{|push| |any| |pop| |push|}&\n5:\cr
2792
 
\hbox{|push| |any| |pop| |pop|}&\RA\hbox{|any| |pop|}&\n6:\cr
2793
 
}$$
2794
 
 
2795
 
@ In order to perform these transformations we need a stack which is
2796
 
indexed by |vf_ptr|, the number of |push| commands without corresponding
2797
 
|pop| in the packet we are building; the |vf_push_loc| array contains
2798
 
the locations in |byte_mem| following such |push| commands.
2799
 
In view of rule~5 consecutive |push| commands are never stored, the
2800
 
|vf_push_num| array is used to count them.
2801
 
The |vf_last| array indicates the type of the last non-discardable item:
2802
 
a character, a rule, or a group enclosed by |push| and |pop|;
2803
 
the |vf_last_end| array points to the ending locations and, if
2804
 
|vf_last<>vf_other|, the |vf_last_loc| array points to the starting
2805
 
locations of these items.
2806
 
 
2807
 
@d vf_set=0 {|vf_set=char_cl|, last item is a |set_char| or \\{set}}
2808
 
@d vf_rule=1 {|vf_rule=rule_cl|, last item is a |set_rule|}
2809
 
@d vf_group=2 {last item is a group enclosed by |push| and |pop|}
2810
 
@d vf_put=3 {last item is a |put|}
2811
 
@d vf_other=4 {last item (if any) is none of the above}
2812
 
 
2813
 
@<Types...@>=
2814
 
@!vf_type=vf_set..vf_other;
2815
 
 
2816
 
@ @<Glob...@>=
2817
 
@!vf_move: array[stack_pointer] of vf_state; {state of |w|, |x|, |y|, and |z|}
2818
 
@!vf_push_loc: array[stack_pointer] of byte_pointer; {end of a |push|}
2819
 
@!vf_last_loc: array[stack_pointer] of byte_pointer; {start of an item}
2820
 
@!vf_last_end: array[stack_pointer] of byte_pointer; {end of an item}
2821
 
@!vf_push_num: array[stack_pointer] of eight_bits; {|push| count}
2822
 
@!vf_last: array[stack_pointer] of vf_type; {type of last item}
2823
 
@!vf_ptr:stack_pointer; {current number of unfinished groups}
2824
 
@!stack_used:stack_pointer; {largest |vf_ptr| or |stack_ptr| value}
2825
 
 
2826
 
@ We use two small arrays to determine the item type of a character or a
2827
 
rule.
2828
 
 
2829
 
@<Glob...@>=
2830
 
@!vf_char_type:array[boolean] of vf_type;
2831
 
@!vf_rule_type:array[boolean] of vf_type;
2832
 
 
2833
 
@ @<Set init...@>=
2834
 
vf_move[0][0][0]:=false; vf_move[0][0][1]:=false;
2835
 
vf_move[0][1][0]:=false; vf_move[0][1][1]:=false;@/
2836
 
stack_used:=0;@/
2837
 
vf_char_type[false]:=vf_put; vf_char_type[true]:=vf_set;@/
2838
 
vf_rule_type[false]:=vf_other; vf_rule_type[true]:=vf_rule;
2839
 
 
2840
 
@ Here we read the first bytes of a character packet from the \.{VF}
2841
 
file and initialize the packet being built in |byte_mem|; the start of
2842
 
the whole packet is stored in |vf_push_loc[0]|. When the character
2843
 
packet is finished, a type is be assigned to it: |vf_simple| if the
2844
 
packet ends with a character of the correct width, or |vf_complex|
2845
 
otherwise. Moreover, if such a packet for a character with
2846
 
extension zero consists of just one character with extension zero and
2847
 
the same residue, and if there is no previous packet, the whole packet
2848
 
is replaced by the empty packet.
2849
 
 
2850
 
@d vf_simple=0 {the packet ends with a character of the correct width}
2851
 
@d vf_complex=vf_simple+1 {otherwise}
2852
 
 
2853
 
@<VF: Build a character packet@>=
2854
 
begin if cur_cmd<long_char then
2855
 
  begin vf_limit:=cur_cmd;
2856
 
  cur_ext:=0; cur_res:=vf_ubyte; vf_wp:=check_width(vf_fix3u);
2857
 
  end
2858
 
else  begin vf_limit:=vf_uquad;
2859
 
  cur_ext:=vf_strio; cur_res:=vf_ubyte; vf_wp:=check_width(vf_fix4);
2860
 
  end;
2861
 
Incr(vf_limit)(vf_loc);
2862
 
vf_push_loc[0]:=byte_ptr; vf_last_end[0]:=byte_ptr;
2863
 
vf_last[0]:=vf_other; vf_ptr:=0;@/
2864
 
start_packet(vf_complex);
2865
 
@<VF: Append \.{DVI} commands to the character packet@>;@/
2866
 
k:=pckt_start[pckt_ptr];
2867
 
if vf_last[0]=vf_put then if cur_wp=vf_wp then
2868
 
  begin decr(byte_mem[k]); {change |vf_complex| into |vf_simple|}
2869
 
  if (byte_mem[k]=bi(0))and@|(vf_push_loc[0]=vf_last_loc[0])and@|
2870
 
    (cur_ext=0)and@|(cur_res=pckt_res) then byte_ptr:=k;
2871
 
  end;
2872
 
build_packet;
2873
 
cur_cmd:=vf_ubyte;
2874
 
end
2875
 
 
2876
 
@ For every \.{DVI} command read from the \.{VF} file some action is
2877
 
performed; in addition the initial |push| and the final |pop| are
2878
 
manufactured here.
2879
 
 
2880
 
@<VF: Append \.{DVI} commands to the character packet@>=
2881
 
vf_cur_fnt:=font_font(cur_fnt); vf_fnt:=vf_cur_fnt;@/
2882
 
last_pop:=false; cur_class:=push_cl; {initial |push|}
2883
 
loop  begin
2884
 
reswitch:case cur_class of
2885
 
  three_cases(char_cl): @<VF: Do a |char|, |rule|, or |xxx|@>;
2886
 
  push_cl: @<VF: Do a |push|@>;
2887
 
  pop_cl: @<VF: Do a |pop|@>;
2888
 
  two_cases(w0_cl):
2889
 
    if vf_move[vf_ptr][0][cur_class-w0_cl] then append_one(cur_cmd);
2890
 
  three_cases(right_cl):
2891
 
    begin pckt_signed(dvi_right_cmd[cur_class],cur_parm);
2892
 
    if cur_class>=w_cl then vf_move[vf_ptr][0][cur_class-w_cl]:=true;
2893
 
    end;
2894
 
  two_cases(y0_cl):
2895
 
    if vf_move[vf_ptr][1][cur_class-y0_cl] then append_one(cur_cmd);
2896
 
  three_cases(down_cl):
2897
 
    begin pckt_signed(dvi_down_cmd[cur_class],cur_parm);
2898
 
    if cur_class>=y_cl then vf_move[vf_ptr][1][cur_class-y_cl]:=true;
2899
 
    end;
2900
 
  fnt_cl: vf_font;
2901
 
  fnt_def_cl: bad_font;
2902
 
  invalid_cl: if cur_cmd<>nop then bad_font;
2903
 
  end; {there are no other cases}
2904
 
  if vf_loc<vf_limit then vf_first_par
2905
 
  else if last_pop then goto done
2906
 
  else  begin cur_class:=pop_cl; last_pop:=true; {final |pop|}
2907
 
    end;
2908
 
  end;
2909
 
done:if (vf_ptr<>0)or(vf_loc<>vf_limit) then bad_font
2910
 
 
2911
 
@ For a |push| we either increase |vf_push_num| or start a new level and
2912
 
append a |push|.
2913
 
 
2914
 
@d incr_stack(#)==
2915
 
if #=stack_used then
2916
 
  if stack_used=stack_size then overflow(str_stack,stack_size)
2917
 
  else incr(stack_used);
2918
 
incr(#)
2919
 
 
2920
 
@<VF: Do a |push|@>=
2921
 
if (vf_ptr>0)and(vf_push_loc[vf_ptr]=byte_ptr) then
2922
 
  begin if vf_push_num[vf_ptr]=255 then overflow(str_stack,255);
2923
 
  incr(vf_push_num[vf_ptr]);
2924
 
  end
2925
 
else  begin incr_stack(vf_ptr);
2926
 
  @<VF: Start a new level@>;
2927
 
  vf_push_num[vf_ptr]:=0;
2928
 
  end
2929
 
 
2930
 
@ @<VF: Start a new level@>=
2931
 
append_one(push);
2932
 
vf_move[vf_ptr]:=vf_move[vf_ptr-1];
2933
 
vf_push_loc[vf_ptr]:=byte_ptr;
2934
 
vf_last_end[vf_ptr]:=byte_ptr;
2935
 
vf_last[vf_ptr]:=vf_other
2936
 
 
2937
 
@ When a character, a rule, or an |xxx| is appended, transformation
2938
 
rule~1 might be applicable.
2939
 
 
2940
 
@<VF: Do a |char|, |rule|, or |xxx|@>=
2941
 
begin if (vf_ptr=0)or(byte_ptr>vf_push_loc[vf_ptr]) then move_zero:=false
2942
 
else case cur_class of
2943
 
char_cl: move_zero:=(not cur_upd)or(vf_cur_fnt<>vf_fnt);
2944
 
rule_cl: move_zero:=not cur_upd;
2945
 
xxx_cl: move_zero:=true;
2946
 
end; {there are no other cases}
2947
 
if move_zero then
2948
 
  begin decr(byte_ptr); decr(vf_ptr);
2949
 
  end;
2950
 
case cur_class of
2951
 
char_cl: @<VF: Do a |fnt|, a |char|, or both@>;
2952
 
rule_cl: @<VF: Do a |rule|@>;
2953
 
xxx_cl: @<VF: Do an |xxx|@>;
2954
 
end; {there are no other cases}
2955
 
vf_last_end[vf_ptr]:=byte_ptr;
2956
 
if move_zero then
2957
 
  begin incr(vf_ptr); append_one(push); vf_push_loc[vf_ptr]:=byte_ptr;
2958
 
  vf_last_end[vf_ptr]:=byte_ptr;
2959
 
  if cur_class=char_cl then if cur_upd then goto reswitch;
2960
 
  end;
2961
 
end
2962
 
 
2963
 
@ A special situation arises if transformation rule~1 is applied to a
2964
 
|fnt_num| of |fnt| command, but not to the |set_char| or \\{set} command
2965
 
following it; in this case |cur_upd| and |move_zero| are both |true| and
2966
 
the |set_char| or \\{set} command will be appended later.
2967
 
 
2968
 
@<VF: Do a |fnt|, a |char|, or both@>=
2969
 
begin if vf_cur_fnt<>vf_fnt then
2970
 
  begin vf_last[vf_ptr]:=vf_other;
2971
 
  pckt_unsigned(fnt1,vf_cur_fnt); vf_fnt:=vf_cur_fnt;
2972
 
  end;
2973
 
if (not move_zero)or(not cur_upd) then
2974
 
  begin vf_last[vf_ptr]:=vf_char_type[cur_upd];
2975
 
  vf_last_loc[vf_ptr]:=byte_ptr;
2976
 
  pckt_char(cur_upd,cur_ext,cur_res);
2977
 
  end;
2978
 
end
2979
 
 
2980
 
@ @<VF: Do a |rule|@>=
2981
 
begin vf_last[vf_ptr]:=vf_rule_type[cur_upd];
2982
 
vf_last_loc[vf_ptr]:=byte_ptr;
2983
 
append_one(dvi_rule_cmd[cur_upd]);
2984
 
pckt_four(cur_v_dimen); pckt_four(cur_h_dimen);
2985
 
end
2986
 
 
2987
 
@ @<VF: Do an |xxx|@>=
2988
 
begin vf_last[vf_ptr]:=vf_other;
2989
 
pckt_unsigned(xxx1,cur_parm); pckt_room(cur_parm);
2990
 
while cur_parm>0 do
2991
 
  begin append_byte(vf_ubyte); decr(cur_parm);
2992
 
  end;
2993
 
end
2994
 
 
2995
 
@ Transformation rules 2--6 are triggered by a |pop|, either read from
2996
 
the \.{VF} file or manufactured at the end of the packet.
2997
 
 
2998
 
@<VF: Do a |pop|@>=
2999
 
begin if vf_ptr<1 then bad_font;
3000
 
byte_ptr:=vf_last_end[vf_ptr]; {this is rule 2}
3001
 
if vf_last[vf_ptr]<=vf_rule then
3002
 
 if vf_last_loc[vf_ptr]=vf_push_loc[vf_ptr] then
3003
 
  @<VF: Prepare for rule 4@>;
3004
 
if byte_ptr=vf_push_loc[vf_ptr] then @<VF: Apply rule 3 or 4@>
3005
 
else  begin if vf_last[vf_ptr]=vf_group then @<VF: Apply rule 6@>;
3006
 
  append_one(pop); decr(vf_ptr); vf_last[vf_ptr]:=vf_group;
3007
 
  vf_last_loc[vf_ptr]:=vf_push_loc[vf_ptr+1]-1;
3008
 
  vf_last_end[vf_ptr]:=byte_ptr;
3009
 
  if vf_push_num[vf_ptr+1]>0 then @<VF: Apply rule 5@>;
3010
 
  end;
3011
 
end
3012
 
 
3013
 
@ In order to implement transformation rule~4, we cancel the |set_char|,
3014
 
\\{set}, or |set_rule|, append a |pop|, and insert a |put| or |put_rule|
3015
 
with the old parameters.
3016
 
 
3017
 
@<VF: Prepare for rule 4@>=
3018
 
begin cur_class:=vf_last[vf_ptr]; cur_upd:=false;
3019
 
byte_ptr:=vf_push_loc[vf_ptr];
3020
 
end
3021
 
 
3022
 
@ @<VF: Apply rule 3 or 4@>=
3023
 
begin if vf_push_num[vf_ptr]>0 then
3024
 
  begin decr(vf_push_num[vf_ptr]);
3025
 
  vf_move[vf_ptr]:=vf_move[vf_ptr-1];
3026
 
  end
3027
 
else  begin decr(byte_ptr); decr(vf_ptr);
3028
 
  end;
3029
 
if cur_class<>pop_cl then goto reswitch; {this is rule 4}
3030
 
end
3031
 
 
3032
 
@ @<VF: Apply rule 6@>=
3033
 
begin Decr(byte_ptr)(2);
3034
 
for k:=vf_last_loc[vf_ptr]+1 to byte_ptr do byte_mem[k-1]:=byte_mem[k];
3035
 
vf_last[vf_ptr]:=vf_other; vf_last_end[vf_ptr]:=byte_ptr;
3036
 
end
3037
 
 
3038
 
@ @<VF: Apply rule 5@>=
3039
 
begin incr(vf_ptr);
3040
 
@<VF: Start a new level@>;
3041
 
decr(vf_push_num[vf_ptr]);
3042
 
end
3043
 
 
3044
 
@ The \.{VF} format specifies that after a character packet invoked by a
3045
 
|set_char| or \\{set} command, ``|h|~is increased by the \.{TFM} width
3046
 
(properly scaled)---just as if a simple character had been typeset'';
3047
 
for |vf_simple| packets this is achieved by changing the final |put|
3048
 
command into |set_char| or \\{set}, but for |vf_complex| packets an
3049
 
explicit movement must be done. This poses a problem for programs,
3050
 
such as \.{DVIcopy}, which write a new \.{DVI} file with all references
3051
 
to characters from virtual fonts replaced by their character packets:
3052
 
The \.{DVItype} program specifies that the horizontal movements after a
3053
 
|set_char| or \\{set} command, after a |set_rule| command, and after one
3054
 
of the commands |right1..x4|, are all treated differently when \.{DVI}
3055
 
units are converted to pixels.
3056
 
 
3057
 
Thus we introduce a slight extension of \.{DVItype}'s pixel rounding
3058
 
algorithm and hope that this extension will become part of the standard
3059
 
\.{DVItype} program in the near future: If a \.{DVI} file contains a
3060
 
|set_rule| command for a rule with the negative height |width_dimen|,
3061
 
then this rule shall be treated in exactly the same way as a ficticious
3062
 
character whose width is the width of that rule; as value of |width_dimen|
3063
 
we choose $-2^{31}$, the smallest signed 32-bit integer.
3064
 
 
3065
 
@<Glob...@>=
3066
 
@!width_dimen:int_32; {vertical dimension of special rules}
3067
 
 
3068
 
@ When initializing |width_dimen| we are careful to avoid arithmetic
3069
 
overflow.
3070
 
 
3071
 
@<Set init...@>=
3072
 
width_dimen:=-@"40000000; Decr(width_dimen)(@"40000000);
3073
 
 
3074
 
@* Terminal communication.
3075
 
When \.{\title} begins, it engages the user in a brief dialog so that
3076
 
various options may be specified. This part of \.{\title} requires
3077
 
nonstandard \PASCAL\ constructions to handle the online interaction; so
3078
 
it may be preferable in some cases to omit the dialog and simply to
3079
 
stick to the default options. On other hand, the system-dependent
3080
 
routines that are needed are not complicated, so it will not be terribly
3081
 
difficult to introduce them; furthermore they are similar to those in
3082
 
\.{DVItype}.
3083
 
 
3084
 
It may be desirable to (optionally) specify all the options in the
3085
 
command line and skip the dialog with the user, provided the operating
3086
 
system permits this. Here we just define the system-indepent part of the
3087
 
code required for this possibility. Since a complete option (a keyword
3088
 
possibly followed by one or several parameters) may have embedded blanks
3089
 
it might be necessary to replace these blanks by some other separator,
3090
 
e.g., by a '/'. Using, e.g., \.{UNIX} style options one might then say
3091
 
$$\.{\title\space-mag/2000 -sel/17.3/5 -sel/47 ...}$$
3092
 
to override the magnification factor that is stated in the \.{DVI} file,
3093
 
and to select five pages starting with the page numbered~17.3 as well as
3094
 
all remaining pages starting with the one numbered~47; alternatively one
3095
 
might simply say
3096
 
$$\.{\title\space- ...}$$
3097
 
to skip the dialog and use the default options.
3098
 
 
3099
 
The system-dependent initialization code should set the |n_opt| variable
3100
 
to the number of options found in the command line.  If |n_opt=0| the
3101
 
|input_ln| procedure defined below will promt the user for options.  If
3102
 
|n_opt>0| the |k_opt| variable will be incremented and another piece of
3103
 
system-dependent code is invoked instead of the dialog; that code should
3104
 
place the value of command line option number |k_opt| as temporary
3105
 
string into the |byte-mem| array.  This process will be repeated until
3106
 
|k_opt=n_opt|, indicating that all command line options have been
3107
 
processed.
3108
 
@^system dependencies@>
3109
 
 
3110
 
@d opt_separator="/" {acts as blank when scanning (command line) options}
3111
 
 
3112
 
@<Set init...@>=
3113
 
n_opt:=0; {change this to indicate the presence of command line options}
3114
 
k_opt:=0; {just in case}
3115
 
 
3116
 
@ The |input_ln| routine waits for the user to type a line at his or her
3117
 
terminal; then it puts ASCII-code equivalents for the characters on that
3118
 
line into the |byte_mem| array as a temporary string. \PASCAL's
3119
 
standard |input| file is used for terminal input, as |output| is used
3120
 
for terminal output.
3121
 
 
3122
 
Since the terminal is being used for both input and output, some systems
3123
 
need a special routine to make sure that the user can see a prompt message
3124
 
before waiting for input based on that message. (Otherwise the message
3125
 
may just be sitting in a hidden buffer somewhere, and the user will have
3126
 
no idea what the program is waiting for.) We shall invoke a system-dependent
3127
 
subroutine |update_terminal| in order to avoid this problem.
3128
 
@^system dependencies@>
3129
 
 
3130
 
@d update_terminal == break(output) {empty the terminal output buffer}
3131
 
@#
3132
 
@d scan_blank(#)== {tests for `blank' when scanning (command line) options}
3133
 
  ((byte_mem[#]=bi(" "))or(byte_mem[#]=bi(opt_separator)))
3134
 
@d scan_skip== {skip `blanks'}
3135
 
  while scan_blank(scan_ptr)and(scan_ptr<byte_ptr) do incr(scan_ptr)
3136
 
@d scan_init== {initialize |scan_ptr|}
3137
 
  byte_mem[byte_ptr]:=bi(" "); scan_ptr:=pckt_start[pckt_ptr-1]; scan_skip
3138
 
 
3139
 
@<Action procedures for |dialog|@>=
3140
 
procedure input_ln; {inputs a line from the terminal}
3141
 
var k:0..terminal_line_length;
3142
 
begin if n_opt=0 then
3143
 
  begin print('Enter option: '); update_terminal; reset(input);
3144
 
  if eoln(input) then read_ln(input);
3145
 
  k:=0; pckt_room(terminal_line_length);
3146
 
  while (k<terminal_line_length)and not eoln(input) do
3147
 
    begin append_byte(xord[input^]); incr(k); get(input);
3148
 
    end;
3149
 
  end
3150
 
else if k_opt<n_opt then
3151
 
  begin incr(k_opt);
3152
 
  {Copy command line option number |k_opt| into |byte_mem| array!}
3153
 
  end;
3154
 
end;
3155
 
 
3156
 
@ The global variable |scan_ptr| is used while scanning the temporary
3157
 
packet; it points to the next byte in |byte_mem| to be examined.
3158
 
 
3159
 
@<Glob...@>=
3160
 
@!n_opt:int_16; {number of options found in command line}
3161
 
@!k_opt:int_16; {number of command line options processed}
3162
 
@!scan_ptr:byte_pointer; {pointer to next byte to be examined}
3163
 
@!sep_char:text_char; {|' '| or |xchr[opt_separator]|}
3164
 
 
3165
 
@ The |scan_keyword| function is used to test for keywords in a character
3166
 
string stored as temporary packet in |byte_mem|; the result is |true|
3167
 
(and |scan_ptr| is updated) if the characters starting at position
3168
 
|scan_ptr| are an abbreviation of a given keyword followed by at least
3169
 
one blank.
3170
 
 
3171
 
@<Action procedures for |dialog|@>=
3172
 
function scan_keyword(@!p:pckt_pointer;@!l:int_7):boolean;
3173
 
var i,@!j,@!k:byte_pointer; {indices into |byte_mem|}
3174
 
begin i:=pckt_start[p]; j:=pckt_start[p+1]; k:=scan_ptr;
3175
 
while (i<j)and((byte_mem[k]=byte_mem[i])or(byte_mem[k]=byte_mem[i]-"a"+"A")) do
3176
 
  begin incr(i); incr(k);
3177
 
  end;
3178
 
if scan_blank(k)and(i-pckt_start[p]>=l) then
3179
 
  begin scan_ptr:=k; scan_skip; scan_keyword:=true;
3180
 
  end
3181
 
else scan_keyword:=false;
3182
 
end;
3183
 
 
3184
 
@ Here is a routine that scans a (possibly signed) integer and computes
3185
 
the decimal value. If no decimal integer starts at |scan_ptr|, the
3186
 
value~0 is returned. The integer should be less than $2^{31}$ in
3187
 
absolute value.
3188
 
 
3189
 
@<Action procedures for |dialog|@>=
3190
 
function scan_int:int_32;
3191
 
var x:int_32; {accumulates the value}
3192
 
@!negative:boolean; {should the value be negated?}
3193
 
begin if byte_mem[scan_ptr]="-" then
3194
 
  begin negative:=true; incr(scan_ptr);
3195
 
  end
3196
 
else negative:=false;
3197
 
x:=0;
3198
 
while (byte_mem[scan_ptr]>="0")and(byte_mem[scan_ptr]<="9") do
3199
 
  begin x:=10*x+byte_mem[scan_ptr]-"0"; incr(scan_ptr);
3200
 
  end;
3201
 
scan_skip;
3202
 
if negative then scan_int:=-x @+ else scan_int:=x;
3203
 
end;
3204
 
 
3205
 
@ The selected options are put into global variables by the |dialog|
3206
 
procedure, which is called just as \.{\title} begins.
3207
 
@^system dependencies@>
3208
 
 
3209
 
@p @<Action procedures for |dialog|@>@;
3210
 
procedure dialog;
3211
 
label exit;
3212
 
var p:pckt_pointer; {packet being created}
3213
 
begin @<Initialize options@>@;
3214
 
loop  begin input_ln; p:=new_packet; scan_init;
3215
 
  if scan_ptr=byte_ptr then
3216
 
    begin flush_packet; return;
3217
 
    end@;@/
3218
 
  @<Cases for options@>@;@/
3219
 
  else  begin if n_opt=0 then sep_char:=' '
3220
 
    else sep_char:=xchr[opt_separator];
3221
 
    print_options;
3222
 
    if n_opt>0 then
3223
 
      begin print('Bad command line option: ');
3224
 
      print_packet(p); abort('---run terminated');
3225
 
      end;
3226
 
    end;
3227
 
  flush_packet;
3228
 
  end;
3229
 
exit:end;
3230
 
 
3231
 
@ The |print_options| procedure might be used in a `Usage message'
3232
 
displaying the command line syntax.
3233
 
 
3234
 
@<Basic printing...@>=
3235
 
procedure print_options;
3236
 
begin print_ln('Valid options are:');
3237
 
@<Print valid options@>@;
3238
 
end;
3239
 
 
3240
 
@* Subroutines for typesetting commands.
3241
 
This is the central part of the whole \.{\title} program:
3242
 
When a typesetting command from the \.{DVI} file or from a \.{VF} packet
3243
 
has been decoded, one of the typesetting routines defined below is
3244
 
invoked to execute the command; apart from the necessary book keeping,
3245
 
these routines invoke device dependent code defined later.
3246
 
 
3247
 
@p @<Declare typesetting procedures@>
3248
 
 
3249
 
@ These typesetting routines communicate with the rest of the program
3250
 
through global variables.
3251
 
 
3252
 
@<Glob...@>=
3253
 
@!type_setting:boolean; {|true| while typesetting a page}
3254
 
 
3255
 
@ @<Set init...@>=
3256
 
type_setting:=false;
3257
 
 
3258
 
@ The user may select up to |max_select| ranges of consecutive pages to
3259
 
be processed. Each starting page specification is recorded in two global
3260
 
arrays called |start_count| and |start_there|. For example, `\.{1.*.-5}'
3261
 
is represented by |start_there[0]=true|, |start_count[0]=1|,
3262
 
|start_there[1]=false|, |start_there[2]=true|, |start_count[2]=-5|. We
3263
 
also set |start_vals=2|, to indicate that count 2 was the last one
3264
 
mentioned. The other values of |start_count| and |start_there| are not
3265
 
important, in this example. The number of pages is recorded in
3266
 
|max_pages|; a non positive value indicates that there is no limit.
3267
 
 
3268
 
@d start_count==select_count[cur_select] {count values to select
3269
 
  starting page}
3270
 
@d start_there==select_there[cur_select] {is the |start_count| value
3271
 
  relevant?}
3272
 
@d start_vals==select_vals[cur_select] {the last count considered
3273
 
  significant}
3274
 
@d max_pages==select_max[cur_select] {at most this many |bop..eop| pages
3275
 
  will be printed}
3276
 
 
3277
 
@<Glob...@>=
3278
 
@!select_count:array[0..max_select-1,0..9] of int_32;
3279
 
@!select_there:array[0..max_select-1,0..9] of boolean;
3280
 
@!select_vals:array[0..max_select-1] of 0..9;
3281
 
@!select_max:array[0..max_select-1] of int_32;
3282
 
@!out_mag:int_32; {output maginfication}
3283
 
@!count:array[0..9] of int_32; {the count values on the current page}
3284
 
@!num_select:0..max_select; {number of page selection ranges specified}
3285
 
@!cur_select:0..max_select; {current page selection range}
3286
 
@!selected:boolean; {has starting page been found?}
3287
 
@!all_done:boolean; {have all selected pages been processed?}
3288
 
@!str_mag,@!str_select:pckt_pointer;
3289
 
 
3290
 
@ Here is a simple subroutine that tests if the current page might be the
3291
 
starting page.
3292
 
 
3293
 
@p function start_match:boolean; {does |count| match the starting spec?}
3294
 
var k:0..9;  {loop index}
3295
 
@!match:boolean; {does everything match so far?}
3296
 
begin match:=true;
3297
 
for k:=0 to start_vals do
3298
 
  if start_there[k]and(start_count[k]<>count[k]) then match:=false;
3299
 
start_match:=match;
3300
 
end;
3301
 
 
3302
 
@ @<Initialize options@>=
3303
 
out_mag:=0; cur_select:=0; max_pages:=0; selected:=true;
3304
 
 
3305
 
@ @<Print valid options@>=
3306
 
print_ln('  mag',sep_char,'<new_mag>');
3307
 
print_ln('  select',sep_char,'<start_count>',sep_char,
3308
 
  '[<max_pages>]  (up to ',max_select:1,' ranges)');
3309
 
 
3310
 
@ @<Action procedures for |dialog|@>=
3311
 
procedure scan_count; {scan a |start_count| value}
3312
 
begin if byte_mem[scan_ptr]=bi("*") then
3313
 
  begin start_there[start_vals]:=false; incr(scan_ptr); scan_skip;
3314
 
  end
3315
 
else  begin start_there[start_vals]:=true;
3316
 
  start_count[start_vals]:=scan_int;
3317
 
  if cur_select=0 then selected:=false; {don't start at first page}
3318
 
  end;
3319
 
end;
3320
 
 
3321
 
@ @<Cases for options@>=
3322
 
else if scan_keyword(str_mag,3) then out_mag:=scan_int
3323
 
else if scan_keyword(str_select,3) then
3324
 
  if cur_select=max_select then print_ln('Too many page selections')
3325
 
  else  begin start_vals:=0; scan_count;
3326
 
    while (start_vals<9)and(byte_mem[scan_ptr]=bi(".")) do
3327
 
      begin incr(start_vals); incr(scan_ptr); scan_count;
3328
 
      end;
3329
 
    max_pages:=scan_int; incr(cur_select);
3330
 
    end
3331
 
 
3332
 
@ @<Initialize predefined strings@>=
3333
 
id3("m")("a")("g")(str_mag);
3334
 
id6("s")("e")("l")("e")("c")("t")(str_select);
3335
 
 
3336
 
@ A stack is used to keep track of the current horizonal and vertical
3337
 
position, |h| and |v|, and the four registers |w|, |x|, |y|, and |z|;
3338
 
the register pairs |(w,x)| and |(y,z)| are maintained as arrays.
3339
 
 
3340
 
@<Types...@>=
3341
 
@!device @<Declare device dependend types@>@; @+ ecived @; @/
3342
 
@!stack_pointer=0..stack_size;@/
3343
 
@!stack_index=1..stack_size;@/
3344
 
@!pair_32=array[0..1] of int_32; {a pair of |int_32| variables}
3345
 
@!stack_record=record@;@/
3346
 
  @!h_field:int_32; {horizontal position |h|}
3347
 
  @!v_field:int_32; {vertical position |v|}
3348
 
  @!w_x_field:pair_32; {|w| and |x| register for horizontal movements}
3349
 
  @!y_z_field:pair_32; {|y| and |z| register for vertical movements}
3350
 
  @!device @<Device dependent stack record fields@>@; @+ ecived @; @/
3351
 
  end;
3352
 
 
3353
 
@ The current values are kept in |cur_stack|; they are pushed onto and
3354
 
popped from |stack|. We use \.{WEB} macros to access the current values.
3355
 
 
3356
 
@d cur_h==cur_stack.h_field {the current |@!h| value}
3357
 
@d cur_v==cur_stack.v_field {the current |@!v| value}
3358
 
@d cur_w_x==cur_stack.w_x_field {the current |@!w| and |@!x| value}
3359
 
@d cur_y_z==cur_stack.y_z_field {the current |@!y| and |@!z| value}
3360
 
 
3361
 
@<Glob...@>=
3362
 
@!stack:array[stack_index] of stack_record; {the pushed values}
3363
 
@!cur_stack:stack_record; {the current values}
3364
 
@!zero_stack:stack_record; {initial values}
3365
 
@!stack_ptr:stack_pointer; {last used position in |stack|}
3366
 
 
3367
 
@ @<Set init...@>=
3368
 
zero_stack.h_field:=0; zero_stack.v_field:=0;
3369
 
for i:=0 to 1 do
3370
 
  begin zero_stack.w_x_field[i]:=0; zero_stack.y_z_field[i]:=0;
3371
 
  end;
3372
 
@!device @<Initialize device dependent stack record fields@>@; @+ ecived @; @/
3373
 
 
3374
 
@ When typesetting for a real device we must convert the current
3375
 
position from \.{DVI} units to pixels, i.e., |cur_h| and |cur_v| into
3376
 
|cur_hh| and |cur_vv|.  This might be a good place to collect everything
3377
 
related to the conversion from \.{DVI} units to pixels and in particular
3378
 
all the pixel rounding algorithms.
3379
 
 
3380
 
@d font_space(#)==fnt_space[#] {boundary between ``small'' and ``large''
3381
 
  spaces}
3382
 
 
3383
 
@<Declare device dependent font data arrays@>=
3384
 
@!fnt_space:array [font_number] of int_32; {boundary between ``small''
3385
 
  and ``large'' spaces}
3386
 
 
3387
 
@ @<Initialize device dependent font data@>=
3388
 
font_space(invalid_font):=0;
3389
 
 
3390
 
@ @<Initialize device dependent data for a font@>=
3391
 
font_space(cur_fnt):=font_scaled(cur_fnt) div 6;
3392
 
  {this is a 3-unit ``thin space''}
3393
 
 
3394
 
@ The |char_pixels| array is used to store the horizontal character
3395
 
escapements:  for \.{PK} or \.{GF} files we use the values given there,
3396
 
otherwise we must convert the character widths to (horizontal) pixels.
3397
 
The horizontal escapement of character~|c| in font~|f| is given by
3398
 
|font_pixel(f)(c)|.
3399
 
 
3400
 
@d font_pixel(#)==char_pixels[font_chars(#)+font_width_end
3401
 
@#
3402
 
@d max_pix_value==@"7FFF {largest allowed pixel value; this range may not
3403
 
  suffice for high resolution output devices}
3404
 
 
3405
 
@<Declare device dependend types@>=
3406
 
@!pix_value=-max_pix_value..max_pix_value; {a pixel coordinate or displacement}
3407
 
 
3408
 
@ @<Glob...@>=
3409
 
@!device
3410
 
@!char_pixels:array[char_pointer] of pix_value; {character escapements}
3411
 
@!h_pixels:pix_value; {a horizontal dimension in pixels}
3412
 
@!v_pixels:pix_value; {a vertical dimension in pixels}
3413
 
@!temp_pix:pix_value; {temporary value for pixel rounding}
3414
 
ecived
3415
 
 
3416
 
@ @d cur_hh==cur_stack.hh_field {the current |@!hh| value}
3417
 
@d cur_vv==cur_stack.vv_field {the current |@!vv| value}
3418
 
 
3419
 
@<Device dependent stack record fields@>=
3420
 
@!hh_field:pix_value; {horizontal pixel position |hh|}
3421
 
@!vv_field:pix_value; {vertical pixel position |vv|}
3422
 
 
3423
 
@ @<Initialize device dependent stack record fields@>=
3424
 
zero_stack.hh_field:=0; zero_stack.vv_field:=0;
3425
 
 
3426
 
@ For small movements we round the increment in position, for large
3427
 
movements we round the incremented position.  The same applies to rule
3428
 
dimensions with the only difference that they will always be rounded
3429
 
towards larger values.  For characters we increment the horizontal
3430
 
position by the escapement values obtained, e.g., from a \.{PK} file or
3431
 
by the \.{TFM} width converted to pixels.
3432
 
 
3433
 
@d h_pixel_round(#)==round(h_conv*(#))
3434
 
@d v_pixel_round(#)==round(v_conv*(#))
3435
 
@^system dependencies@>
3436
 
@#
3437
 
@d large_h_space(#)==(#>=font_space(cur_fnt))or(#<=-4*font_space(cur_fnt))
3438
 
  {is this a ``large'' horizontal distance?}
3439
 
@d large_v_space(#)==(abs(#)>=5*font_space(cur_fnt))
3440
 
  {is this a ``large'' vertical distance?}
3441
 
@#
3442
 
@d h_rule_pixels== {converts the rule width |cur_h_dimen| to pixels}
3443
 
@!device if large_h_space(cur_h_dimen) then
3444
 
  begin h_pixels:=h_pixel_round(cur_h+cur_h_dimen)-cur_hh;
3445
 
  if h_pixels<=0 then if cur_h_dimen>0 then h_pixels:=1;
3446
 
  end
3447
 
else  begin h_pixels:=trunc(h_conv*cur_h_dimen);
3448
 
  if h_pixels<h_conv*cur_h_dimen then incr(h_pixels);
3449
 
  end;
3450
 
ecived
3451
 
@#
3452
 
@d v_rule_pixels== {converts the rule height |cur_v_dimen| to pixels}
3453
 
@!device if large_v_space(cur_v_dimen) then
3454
 
  begin v_pixels:=cur_vv-v_pixel_round(cur_v-cur_v_dimen);
3455
 
  if v_pixels<=0 then v_pixels:=1; {used only for |cur_v_dimen>0|}
3456
 
  end
3457
 
else  begin v_pixels:=trunc(v_conv*cur_v_dimen);
3458
 
  if v_pixels<v_conv*cur_v_dimen then incr(v_pixels);
3459
 
  end;
3460
 
ecived
3461
 
 
3462
 
@ A sequence of consecutive rules, or consecutive characters in a
3463
 
fixed-width font whose width is not an integer number of pixels, can
3464
 
cause |hh| to drift far away from a correctly rounded value.  \.{\title}
3465
 
ensures that the amount of drift will never exceed |max_h_drift| pixels;
3466
 
similarly |vv| shall never drift away from the correctly rounded value
3467
 
by more than |max_v_drift| pixels.
3468
 
 
3469
 
@d h_upd_end(#)== {check for proper horizontal pixel rounding}
3470
 
begin Incr(cur_hh)(#); temp_pix:=h_pixel_round(cur_h);
3471
 
if abs(temp_pix-cur_hh)>max_h_drift then
3472
 
  if temp_pix>cur_hh then cur_hh:=temp_pix-max_h_drift
3473
 
  else cur_hh:=temp_pix+max_h_drift;
3474
 
end @+ ecived
3475
 
@d h_upd_char(#)==Incr(cur_h)(#)@;
3476
 
  @!device; h_upd_end
3477
 
@d h_upd_move(#)==Incr(cur_h)(#)@;
3478
 
  @!device; if large_h_space(#) then cur_hh:=h_pixel_round(cur_h)
3479
 
  else h_upd_end
3480
 
@#
3481
 
@d v_upd_end(#)== {check for proper vertical pixel rounding}
3482
 
begin Incr(cur_vv)(#); temp_pix:=v_pixel_round(cur_v);
3483
 
if abs(temp_pix-cur_vv)>max_v_drift then
3484
 
  if temp_pix>cur_vv then cur_vv:=temp_pix-max_v_drift
3485
 
  else cur_vv:=temp_pix+max_v_drift;
3486
 
end @+ ecived
3487
 
@d v_upd_move(#)==Incr(cur_v)(#)@;
3488
 
  @!device; if large_v_space(#) then cur_vv:=v_pixel_round(cur_v)
3489
 
  else v_upd_end
3490
 
 
3491
 
@ The routines defined below use sections named `Declare local variables
3492
 
(if any) for \dots' or `Declare additional local variables for \dots';
3493
 
the former may declare variables (including the keyword \&{var}), whereas
3494
 
the later must at least contain the keyword \&{var}. In general, both may
3495
 
start with the declaration of labels, constants, and\slash or types.
3496
 
 
3497
 
Let us start with the simple cases:
3498
 
The |do_pre| procedure is called when the preamble has been read from
3499
 
the \.{DVI} file; the preamble comment has just been converted into a
3500
 
temporary packet with the |new_packet| procedure.
3501
 
 
3502
 
@p procedure do_pre;@/
3503
 
@<OUT: Declare local variables (if any) for |do_pre|@>@;
3504
 
begin all_done:=false; num_select:=cur_select; cur_select:=0;
3505
 
if num_select=0 then max_pages:=0;
3506
 
@!device
3507
 
h_conv:=(dvi_num/254000.0)*(h_resolution/dvi_den)*(out_mag/1000.0);
3508
 
v_conv:=(dvi_num/254000.0)*(v_resolution/dvi_den)*(out_mag/1000.0);
3509
 
ecived @; @/
3510
 
@<OUT: Process the |pre|@>@;@/
3511
 
end;
3512
 
 
3513
 
@ The |do_bop| procedure is called when a |bop| has been read. This
3514
 
routine determines whether a page shall be processed or skipped and sets
3515
 
the variable |type_setting| accordingly.
3516
 
 
3517
 
@p procedure do_bop;@/
3518
 
@<OUT: Declare additional local variables |do_bop|@>@;
3519
 
@!i,@!j:0..9; {indices into |count|}
3520
 
begin @<Determine whether this page should be processed or skipped@>;
3521
 
print('DVI: ');
3522
 
if type_setting then print('process') @+ else print('skipp');
3523
 
print('ing page ',count[0]:1); j:=9;
3524
 
while (j>0)and(count[j]=0) do decr(j);
3525
 
for i:=1 to j do print('.',count[i]:1);
3526
 
d_print(' at ',dvi_loc-45:1);
3527
 
print_ln('.');
3528
 
if type_setting then
3529
 
  begin stack_ptr:=0; cur_stack:=zero_stack; cur_fnt:=invalid_font;@/
3530
 
  @<OUT: Process a |bop|@>@;@/
3531
 
  end;
3532
 
end;
3533
 
 
3534
 
@ Note that the device dependent code `OUT: Process a |bop|' may choose
3535
 
to set |type_setting| to false even if |selected| is true.
3536
 
 
3537
 
@<Determine whether this page...@>=
3538
 
if not selected then selected:=start_match;
3539
 
type_setting:=selected
3540
 
 
3541
 
@ The |do_eop| procedure is called in order to process an |eop|; the
3542
 
stack should be empty.
3543
 
 
3544
 
@p procedure do_eop;@/
3545
 
@<OUT: Declare local variables (if any) for |do_eop|@>@;
3546
 
begin if stack_ptr<>0 then bad_dvi;
3547
 
@<OUT: Process an |eop|@>@;
3548
 
if max_pages>0 then
3549
 
  begin decr(max_pages);
3550
 
  if max_pages=0 then
3551
 
    begin selected:=false; incr(cur_select);
3552
 
   if cur_select=num_select then all_done:=true;
3553
 
    end;
3554
 
  end;
3555
 
type_setting:=false;
3556
 
end;
3557
 
 
3558
 
@ The procedures |do_push| and |do_pop| are called in order to process
3559
 
|push| and |pop| commands; |do_push| must check for stack overflow,
3560
 
|do_pop| should never be called when the stack is empty.
3561
 
 
3562
 
@p procedure do_push; {push onto stack}
3563
 
@<OUT: Declare local variables (if any) for |do_push|@>@;
3564
 
begin incr_stack(stack_ptr); stack[stack_ptr]:=cur_stack;@/
3565
 
@<OUT: Process a |push|@>@;
3566
 
end;
3567
 
@#
3568
 
procedure do_pop; {pop from stack}
3569
 
@<OUT: Declare local variables (if any) for |do_pop|@>@;
3570
 
begin if stack_ptr=0 then bad_dvi;
3571
 
cur_stack:=stack[stack_ptr]; decr(stack_ptr);
3572
 
@<OUT: Process a |pop|@>@;@/
3573
 
end;
3574
 
 
3575
 
@ The |do_xxx| procedure is called in order to process a special command.
3576
 
The bytes of the special string have been put into |byte_mem| as the
3577
 
current string. They are converted to a temporary packet and discarded
3578
 
again.
3579
 
 
3580
 
@p procedure do_xxx;@/
3581
 
@<OUT: Declare additional local variables for |do_xxx|@>@;
3582
 
@!p:pckt_pointer; {temporary packet}
3583
 
begin p:=new_packet;@/
3584
 
@<OUT: Process an |xxx|@>@;@/
3585
 
flush_packet;
3586
 
end;
3587
 
 
3588
 
@ Next are the movement commands:
3589
 
The |do_right| procedure is called in order to process the horizontal
3590
 
movement commands |right|, |w|, and |x|.
3591
 
 
3592
 
 
3593
 
@p procedure do_right;@/
3594
 
@<OUT: Declare local variables (if any) for |do_right|@>@;
3595
 
begin if cur_class>=w_cl then cur_w_x[cur_class-w_cl]:=cur_parm
3596
 
else if cur_class<right_cl then cur_parm:=cur_w_x[cur_class-w0_cl];
3597
 
@<OUT: Process a |right| or |w| or |x|@>@;@/
3598
 
h_upd_move(cur_parm)(h_pixel_round(cur_parm));
3599
 
@<OUT: Move right@>@;
3600
 
end;
3601
 
 
3602
 
@ The |do_down| procedure is called in order to process the vertical
3603
 
movement commands |down|, |y|, and |z|.
3604
 
 
3605
 
@p procedure do_down;@/
3606
 
@<OUT: Declare local variables (if any) for |do_down|@>@;
3607
 
begin if cur_class>=y_cl then cur_y_z[cur_class-y_cl]:=cur_parm
3608
 
else if cur_class<down_cl then cur_parm:=cur_y_z[cur_class-y0_cl];
3609
 
@<OUT: Process a |down| or |y| or |z|@>@;@/
3610
 
v_upd_move(cur_parm)(v_pixel_round(cur_parm));
3611
 
@<OUT: Move down@>@;
3612
 
end;
3613
 
 
3614
 
@ The |do_width| procedure, or actually the |do_a_width| macro, is
3615
 
called in order to increase the current horizontal position |cur_h| by
3616
 
|cur_h_dimen| in exactly the same way as if a character of width
3617
 
|cur_h_dimen| had been typeset.
3618
 
 
3619
 
@d do_a_width(#)==
3620
 
  begin @!device h_pixels:=#; @+ ecived @; @+ do_width;
3621
 
  end
3622
 
 
3623
 
@p procedure do_width;@/
3624
 
@<OUT: Declare local variables (if any) for |do_width|@>@;
3625
 
begin @<OUT: Typeset a |width|@>@;@/
3626
 
h_upd_char(cur_h_dimen)(h_pixels);
3627
 
@<OUT: Move right@>@;
3628
 
end;
3629
 
 
3630
 
@ Finally we have the commands for the typesetting of rules and characters;
3631
 
the global variable |cur_upd| is |true| if the horizontal position shall
3632
 
be updated (\\{set} commands).
3633
 
 
3634
 
The |do_rule| procedure is called in order to typeset a rule.
3635
 
 
3636
 
@p procedure do_rule;@/
3637
 
@<OUT: Declare additional local variables |do_rule|@>@;
3638
 
@!visible:boolean;
3639
 
begin h_rule_pixels@;
3640
 
if (cur_h_dimen>0)and(cur_v_dimen>0) then
3641
 
  begin visible:=true; v_rule_pixels@;
3642
 
  @<OUT: Typeset a visible |rule|@>@;
3643
 
  end
3644
 
else  begin visible:=false;
3645
 
  @<OUT: Typeset an invisible |rule|@>@;
3646
 
  end;
3647
 
if cur_upd then
3648
 
  begin h_upd_move(cur_h_dimen)(h_pixels);
3649
 
  @<OUT: Move right@>@;
3650
 
  end;
3651
 
end;
3652
 
 
3653
 
@ Last not least the |do_char| procedure is called in order to typeset
3654
 
character~|cur_res| with extension~|cur_ext| from the real font~|cur_fnt|.
3655
 
 
3656
 
@p procedure do_char;@/
3657
 
@<OUT: Declare local variables (if any) for |do_char|@>@;
3658
 
begin @<OUT: Typeset a |char|@>@;
3659
 
if cur_upd then
3660
 
  begin h_upd_char(widths[cur_wp])(char_pixels[cur_cp]);
3661
 
  @<OUT: Move right@>@;
3662
 
  end;
3663
 
end;
3664
 
 
3665
 
@ If the program terminates abnormally, the following code may be
3666
 
invoked in the middle of a page.
3667
 
 
3668
 
@<Finish output file(s)@>=
3669
 
begin if type_setting then @<OUT: Finish incomplete page@>;
3670
 
@<OUT: Finish output file(s)@>@;
3671
 
end
3672
 
 
3673
 
@ When the first character of font~|cur_fnt| is about to be typeset, the
3674
 
|do_font| procedure is called in order to decide whether this is a
3675
 
virtual font or a real font.
3676
 
 
3677
 
One step in this decision is the attempt to find and read the \.{VF}
3678
 
file for this font; other attempts to locate a font file may be
3679
 
performed before and after that, depending on the nature of the output
3680
 
device and on the structure of the file system at a particular
3681
 
installation.  For a real device we convert the character widths to
3682
 
(horizontal) pixels.
3683
 
 
3684
 
In any case |do_font| must change |font_type(cur_fnt)| to a value
3685
 
|>defined_font|; as a last resort one might use the \.{TFM} width data
3686
 
and draw boxes or leave blank spaces in the output.
3687
 
 
3688
 
@p procedure do_font;@/
3689
 
label done;@/
3690
 
@<OUT: Declare additional local variables for |do_font|@>@;
3691
 
@!p:char_pointer; {index into |char_widths| and |char_pixels|}
3692
 
begin @!debug if font_type(cur_fnt)=defined_font then confusion(str_fonts);
3693
 
gubed@;
3694
 
@!device for p:=font_chars(cur_fnt)+font_bc(cur_fnt)
3695
 
  to font_chars(cur_fnt)+font_ec(cur_fnt) do
3696
 
    char_pixels[p]:=h_pixel_round(widths[char_widths[p]]);
3697
 
ecived@;
3698
 
@<OUT: Look for a font file before trying to read the \.{VF} file;
3699
 
  if found |goto done|@>@;@/
3700
 
if do_vf then goto done; {try to read the \.{VF} file}
3701
 
@<OUT: Look for a font file after trying to read the \.{VF} file@>@;@/
3702
 
done:
3703
 
@!debug if font_type(cur_fnt)<=loaded_font then confusion(str_fonts);
3704
 
gubed@;
3705
 
end;
3706
 
 
3707
 
@ Before a character of font~|cur_fnt| is typeset the following piece of
3708
 
code ensures that the font is ready to be used.
3709
 
 
3710
 
@<Prepare to use font |cur_fnt|@>=
3711
 
@<OUT: Prepare to use font |cur_fnt|@>@;
3712
 
if font_type(cur_fnt)<=loaded_font then do_font {|cur_fnt| was not yet used}
3713
 
 
3714
 
@* Interpreting VF packets.
3715
 
The |pckt_first_par| procedure first reads a \.{DVI} command byte from
3716
 
the packet into |cur_cmd|; then |cur_parm| is set to the value of the
3717
 
first parameter (if any) and |cur_class| to the command class.
3718
 
 
3719
 
@p procedure pckt_first_par;
3720
 
begin cur_cmd:=pckt_ubyte;
3721
 
case dvi_par[cur_cmd] of
3722
 
char_par: set_cur_char(pckt_ubyte);
3723
 
no_par: do_nothing;
3724
 
dim1_par: cur_parm:=pckt_sbyte;
3725
 
num1_par: cur_parm:=pckt_ubyte;
3726
 
dim2_par: cur_parm:=pckt_spair;
3727
 
num2_par: cur_parm:=pckt_upair;
3728
 
dim3_par: cur_parm:=pckt_strio;
3729
 
num3_par: cur_parm:=pckt_utrio;
3730
 
three_cases(dim4_par): cur_parm:=pckt_squad; {|dim4|, |num4|, or |numu|}
3731
 
rule_par:
3732
 
  begin cur_v_dimen:=pckt_squad; cur_h_dimen:=pckt_squad;
3733
 
  cur_upd:=(cur_cmd=set_rule);
3734
 
  end;
3735
 
fnt_par:cur_parm:=cur_cmd-fnt_num_0;
3736
 
end; {there are no other cases}
3737
 
cur_class:=dvi_cl[cur_cmd];
3738
 
end;
3739
 
 
3740
 
@ The |do_vf_packet| procedure is called in order to interpret the
3741
 
character packet for a virtual character. Such a packet may contain the
3742
 
instruction to typeset a character from the same or an other virtual
3743
 
font; in such cases |do_vf_packet| calls itself recursively. The
3744
 
recursion level, i.e., the number of times this has happened, is kept
3745
 
in the global variable |n_recur| and should not exceed |max_recursion|.
3746
 
@^recursion@>
3747
 
 
3748
 
@<Types...@>=
3749
 
@!recur_pointer=0..max_recursion;
3750
 
 
3751
 
@ The \.{\title} processor should detect an infinite recursion caused by
3752
 
bad \.{VF} files; thus a new recursion level is entered even in cases
3753
 
where this could be avoided without difficulty.
3754
 
 
3755
 
If the recursion level exceeds the allowed maximum, we want to give
3756
 
a traceback how this has happened; thus some of the global variables
3757
 
used in different invocations of |do_vf_packet| are saved in a stack,
3758
 
others are saved as local variables of |do_vf_packet|.
3759
 
 
3760
 
@<Glob...@>=
3761
 
@!recur_fnt:array[recur_pointer] of font_number; {this packet's font}
3762
 
@!recur_ext:array[recur_pointer] of int_24; {this packet's extension}
3763
 
@!recur_res:array[recur_pointer] of eight_bits; {this packet's residue}
3764
 
@!recur_pckt:array[recur_pointer] of pckt_pointer; {the packet}
3765
 
@!recur_loc:array[recur_pointer] of byte_pointer; {next byte of packet}
3766
 
@!n_recur:recur_pointer; {current recursion level}
3767
 
@!recur_used:recur_pointer; {highest recursion level used so far}
3768
 
 
3769
 
@ @<Set init...@>=
3770
 
n_recur:=0; recur_used:=0;
3771
 
 
3772
 
@ Here now is the |do_vf_packet| procedure.
3773
 
 
3774
 
@p procedure do_vf_packet;
3775
 
label continue,found,done;
3776
 
var k:recur_pointer; {loop index}
3777
 
@!f:int_8u; {packet type flag}
3778
 
@!save_upd:boolean; {used to save |cur_upd|}
3779
 
@!save_cp:width_pointer; {used to save |cur_cp|}
3780
 
@!save_wp:width_pointer; {used to save |cur_wp|}
3781
 
@!save_limit:byte_pointer; {used to save |cur_limit|}
3782
 
begin @<VF: Save values on entry to |do_vf_packet|@>;@/
3783
 
@<VF: Interpret the \.{DVI} commands in the packet@>@;@/
3784
 
if save_upd then
3785
 
  begin cur_h_dimen:=widths[save_wp]; do_a_width(char_pixels[save_cp]);
3786
 
  end;
3787
 
@<VF: Restore values on exit from |do_vf_packet|@>;@/
3788
 
end;
3789
 
 
3790
 
@ On entry to |do_vf_packet| several values must be saved.
3791
 
 
3792
 
@<VF: Save values on entry to |do_vf_packet|@>=
3793
 
save_upd:=cur_upd; save_cp:=cur_cp; save_wp:=cur_wp;@/
3794
 
recur_fnt[n_recur]:=cur_fnt;
3795
 
recur_ext[n_recur]:=cur_ext;
3796
 
recur_res[n_recur]:=cur_res
3797
 
 
3798
 
@ Some of these values must be restored on exit from |do_vf_packet|.
3799
 
 
3800
 
@<VF: Restore values on exit from |do_vf_packet|@>=
3801
 
cur_fnt:=recur_fnt[n_recur]
3802
 
 
3803
 
@ If |cur_pckt| is the empty packet, we manufacture a |put| command;
3804
 
otherwise we read and interpret \.{DVI} commands from the packet.
3805
 
 
3806
 
@<VF: Interpret the \.{DVI} commands in the packet@>=
3807
 
if find_packet then f:=cur_type @+ else goto done;
3808
 
recur_pckt[n_recur]:=cur_pckt;
3809
 
save_limit:=cur_limit;
3810
 
cur_fnt:=font_font(cur_fnt);
3811
 
if cur_pckt=empty_packet then
3812
 
  begin cur_class:=char_cl; goto found;
3813
 
  end;
3814
 
if cur_loc>=cur_limit then goto done;
3815
 
continue: pckt_first_par;
3816
 
found: case cur_class of
3817
 
char_cl: @<VF: Typeset a |char|@>;
3818
 
rule_cl: do_rule;
3819
 
xxx_cl:
3820
 
  begin pckt_room(cur_parm);
3821
 
  while cur_parm>0 do
3822
 
    begin append_byte(pckt_ubyte); decr(cur_parm);
3823
 
    end;
3824
 
  do_xxx;
3825
 
  end;
3826
 
push_cl: do_push;
3827
 
pop_cl: do_pop;
3828
 
five_cases(w0_cl): do_right; {|right|, |w|, or |x|}
3829
 
five_cases(y0_cl): do_down; {|down|, |y|, or |z|}
3830
 
fnt_cl: cur_fnt:=cur_parm;
3831
 
othercases confusion(str_packets); {font definition or invalid}
3832
 
endcases;
3833
 
if cur_loc<cur_limit then goto continue;
3834
 
done:
3835
 
 
3836
 
@ The final |put| of a simple packet may be changed into |set_char| or
3837
 
\\{set}.
3838
 
 
3839
 
@<VF: Typeset a |char|@>=
3840
 
begin @<Prepare to use font |cur_fnt|@>;
3841
 
cur_cp:=font_chars(cur_fnt)+cur_res; cur_wp:=char_widths[cur_cp];
3842
 
if (cur_loc=cur_limit)and(f=vf_simple) and save_upd then
3843
 
  begin save_upd:=false; cur_upd:=true;
3844
 
  end;
3845
 
if font_type(cur_fnt)=vf_font_type then
3846
 
  @<VF: Enter a new recursion level@>
3847
 
else do_char;
3848
 
end
3849
 
 
3850
 
@ Before entering a new recursion level we must test for overflow; in
3851
 
addition a few variables must be saved and restored.
3852
 
A |set_char| or \\{set} followed by |pop| is changed into |put|.
3853
 
 
3854
 
@<VF: Enter a new recursion level@>=
3855
 
begin recur_loc[n_recur]:=cur_loc; {save}
3856
 
if cur_loc<cur_limit then
3857
 
  if byte_mem[cur_loc]=bi(pop) then cur_upd:=false;
3858
 
if n_recur=recur_used then
3859
 
  if recur_used=max_recursion then
3860
 
    @<VF: Display the recursion traceback and terminate@>
3861
 
  else incr(recur_used);@/
3862
 
incr(n_recur); do_vf_packet; decr(n_recur); {recurse}
3863
 
cur_loc:=recur_loc[n_recur]; cur_limit:=save_limit; {restore}
3864
 
end
3865
 
 
3866
 
@ @<VF: Display the recursion traceback and terminate@>=
3867
 
begin print_ln(' !Infinite VF recursion?');
3868
 
@.Infinite VF recursion?@>
3869
 
for k:=max_recursion downto 0 do
3870
 
  begin print('level=',k:1,' font');
3871
 
  d_print('=',recur_fnt[k]:1);
3872
 
  print_font(recur_fnt[k]);
3873
 
  print(' char=',recur_res[k]:1);
3874
 
  if recur_ext[k]<>0 then print('.',recur_ext[k]:1);
3875
 
  new_line;
3876
 
  @!debug hex_packet(recur_pckt[k]); print_ln('loc=',recur_loc[k]:1);
3877
 
  gubed@;
3878
 
  end;
3879
 
overflow(str_recursion,max_recursion);
3880
 
end
3881
 
 
3882
 
@* Interpreting the DVI file.
3883
 
The |do_dvi| procedure reads the entire \.{DVI} file and initiates
3884
 
whatever actions may be necessary.
3885
 
 
3886
 
@p procedure do_dvi;
3887
 
label done,exit;
3888
 
var temp_byte:int_8u; {byte for temporary variables}
3889
 
@!temp_int:int_32; {integer for temporary variables}
3890
 
@!dvi_start:int_32; {starting location}
3891
 
@!dvi_bop_post:int_32; {location of |bop| or |post|}
3892
 
@!dvi_back:int_32; {a back pointer}
3893
 
@!k:int_15; {general purpose variable}
3894
 
begin @<DVI: Process the preamble@>;
3895
 
if random_reading then @<DVI: Process the postamble@>;
3896
 
repeat dvi_first_par;
3897
 
  while cur_class=fnt_def_cl do
3898
 
    begin dvi_do_font(random_reading); dvi_first_par;
3899
 
    end;
3900
 
  if cur_cmd=bop then @<DVI: Process one page@>;
3901
 
until cur_cmd<>eop;
3902
 
if cur_cmd<>post then bad_dvi;
3903
 
exit:end;
3904
 
 
3905
 
@ @<DVI: Process the preamble@>=
3906
 
if dvi_ubyte<>pre then bad_dvi;
3907
 
if dvi_ubyte<>dvi_id then bad_dvi;
3908
 
dvi_num:=dvi_pquad; dvi_den:=dvi_pquad; dvi_mag:=dvi_pquad;
3909
 
tfm_conv:=(25400000.0/dvi_num)*(dvi_den/473628672)/16.0;
3910
 
temp_byte:=dvi_ubyte; pckt_room(temp_byte);
3911
 
for k:=1 to temp_byte do append_byte(dvi_ubyte);
3912
 
print('DVI file: '''); print_packet(new_packet); print_ln(''',');
3913
 
print('   num=',dvi_num:1,', den=',dvi_den:1,', mag=',dvi_mag:1);
3914
 
if out_mag<=0 then out_mag:=dvi_mag @+ else print(' => ',out_mag:1);
3915
 
print_ln('.');
3916
 
do_pre; flush_packet
3917
 
 
3918
 
@ @<Glob...@>=
3919
 
@!dvi_num:int_31; {numerator}
3920
 
@!dvi_den:int_31; {denominator}
3921
 
@!dvi_mag:int_31; {magnification}
3922
 
 
3923
 
@ @<DVI: Process the postamble@>=
3924
 
begin dvi_start:=dvi_loc; {remember start of first page}
3925
 
@<DVI: Find the postamble@>;
3926
 
d_print_ln('DVI: postamble at ',dvi_bop_post:1);
3927
 
dvi_back:=dvi_pointer;
3928
 
if dvi_num<>dvi_pquad then bad_dvi;
3929
 
if dvi_den<>dvi_pquad then bad_dvi;
3930
 
if dvi_mag<>dvi_pquad then bad_dvi;
3931
 
temp_int:=dvi_squad; temp_int:=dvi_squad;
3932
 
if stack_size<dvi_upair then overflow(str_stack,stack_size);
3933
 
temp_int:=dvi_upair;
3934
 
dvi_first_par;
3935
 
while cur_class=fnt_def_cl do
3936
 
  begin dvi_do_font(false); dvi_first_par;
3937
 
  end;
3938
 
if cur_cmd<>post_post then bad_dvi;
3939
 
if not selected then @<DVI: Find the starting page@>;
3940
 
dvi_move(dvi_start); {go to first or starting page}
3941
 
end
3942
 
 
3943
 
@ @<DVI: Find the postamble@>=
3944
 
temp_int:=dvi_length-5;
3945
 
repeat if temp_int<49 then bad_dvi;
3946
 
dvi_move(temp_int); temp_byte:=dvi_ubyte; decr(temp_int);
3947
 
until temp_byte<>dvi_pad;
3948
 
if temp_byte<>dvi_id then bad_dvi;
3949
 
dvi_move(temp_int-4); if dvi_ubyte<>post_post then bad_dvi;
3950
 
dvi_bop_post:=dvi_pointer;
3951
 
if (dvi_bop_post<15)or(dvi_bop_post>dvi_loc-34) then bad_dvi;
3952
 
dvi_move(dvi_bop_post); if dvi_ubyte<>post then bad_dvi
3953
 
 
3954
 
@ @<DVI: Find the starting page@>=
3955
 
begin dvi_start:=dvi_bop_post; {just in case}
3956
 
while dvi_back<>-1 do
3957
 
  begin if (dvi_back<15)or(dvi_back>dvi_bop_post-46) then bad_dvi;
3958
 
  dvi_bop_post:=dvi_back; dvi_move(dvi_back);
3959
 
  if dvi_ubyte<>bop then bad_dvi;
3960
 
  for k:=0 to 9 do count[k]:=dvi_squad;
3961
 
  if start_match then dvi_start:=dvi_bop_post;
3962
 
  dvi_back:=dvi_pointer;
3963
 
  end;
3964
 
end
3965
 
 
3966
 
@ When a |bop| has been read, the \.{DVI} commands for one page are
3967
 
interpreted until an |eop| is found.
3968
 
 
3969
 
@<DVI: Process one page@>=
3970
 
begin for k:=0 to 9 do count[k]:=dvi_squad;
3971
 
temp_int:=dvi_pointer; do_bop;
3972
 
dvi_first_par;
3973
 
if type_setting then @<DVI: Process a page; then |goto done|@>
3974
 
else @<DVI: Skip a page; then |goto done|@>;
3975
 
done:if cur_cmd<>eop then bad_dvi;
3976
 
if selected then
3977
 
  begin do_eop;
3978
 
  if all_done then return;
3979
 
  end;
3980
 
end
3981
 
 
3982
 
@ All \.{DVI} commands are processed, as long as |cur_class<>invalid_cl|;
3983
 
then we should have found an |eop|.
3984
 
 
3985
 
@<DVI: Process a page; then |goto done|@>=
3986
 
loop begin
3987
 
  case cur_class of
3988
 
  char_cl: @<DVI: Typeset a |char|@>;
3989
 
  rule_cl:
3990
 
    if cur_upd and(cur_v_dimen=width_dimen) then
3991
 
      do_a_width(h_pixel_round(cur_h_dimen))
3992
 
    else do_rule;
3993
 
  xxx_cl:
3994
 
    begin pckt_room(cur_parm);
3995
 
    while cur_parm>0 do
3996
 
      begin append_byte(dvi_ubyte); decr(cur_parm);
3997
 
      end;
3998
 
    do_xxx;
3999
 
    end;
4000
 
  push_cl: do_push;
4001
 
  pop_cl: do_pop;
4002
 
  five_cases(w0_cl): do_right; {|right|, |w|, or |x|}
4003
 
  five_cases(y0_cl): do_down; {|down|, |y|, or |z|}
4004
 
  fnt_cl: dvi_font;
4005
 
  fnt_def_cl: dvi_do_font(random_reading);
4006
 
  invalid_cl: goto done;
4007
 
  end; {there are no other cases}
4008
 
dvi_first_par; {get the next command}
4009
 
end
4010
 
 
4011
 
@ While skipping a page all commands other than font definitions are
4012
 
ignored.
4013
 
 
4014
 
@<DVI: Skip a page; then |goto done|@>=
4015
 
loop begin
4016
 
  case cur_class of
4017
 
  xxx_cl: while cur_parm>0 do
4018
 
    begin temp_byte:=dvi_ubyte; decr(cur_parm);
4019
 
    end;
4020
 
  fnt_def_cl: dvi_do_font(random_reading);
4021
 
  invalid_cl: goto done;
4022
 
  othercases do_nothing;
4023
 
  endcases;
4024
 
dvi_first_par; {get the next command}
4025
 
end
4026
 
 
4027
 
@ @<DVI: Typeset a |char|@>=
4028
 
begin @<Prepare to use font |cur_fnt|@>;
4029
 
set_cur_wp(cur_fnt)(bad_dvi);
4030
 
if font_type(cur_fnt)=vf_font_type then do_vf_packet @+ else do_char;
4031
 
end
4032
 
 
4033
 
@* The main program.
4034
 
The code for real devices is still rather incomplete.
4035
 
Moreover several branches of the program have not been tested because
4036
 
they are never used with \.{DVI} files made by \TeX\ and \.{VF} files
4037
 
made by \.{VPtoVF}.
4038
 
 
4039
 
@ At the end of the program the output file(s) have to be finished and
4040
 
on some systems it may be necessary to close input and\slash or output
4041
 
files.
4042
 
@^system dependencies@>
4043
 
 
4044
 
@p procedure close_files_and_terminate;
4045
 
var k:@!int_15; {general purpose index}
4046
 
begin close_in(dvi_file);
4047
 
if history<fatal_message then @<Finish output file(s)@>;
4048
 
stat @<Print memory usage statistics@>;@+tats@;@/
4049
 
@<Close output file(s)@>@;
4050
 
@<Print the job |history|@>;
4051
 
end;
4052
 
 
4053
 
@ Now we are ready to put it all together.
4054
 
Here is where \.{\title} starts, and where it ends.
4055
 
@^system dependencies@>
4056
 
 
4057
 
@p begin initialize; {get all variables initialized}
4058
 
@<Initialize predefined strings@>@;
4059
 
dialog; {get options}
4060
 
@<Open input file(s)@>@;
4061
 
@<Open output file(s)@>@;
4062
 
do_dvi; {process the entire \.{DVI} file}
4063
 
close_files_and_terminate;
4064
 
final_end:end.
4065
 
 
4066
 
@ @<Print memory usage statistics@>=
4067
 
print_ln('Memory usage statistics:');
4068
 
print(dvi_nf:1,' dvi, ',lcl_nf:1,' local, ');
4069
 
@<Print more font usage statistics@>@;@/
4070
 
print_ln('and ',nf:1,' internal fonts of ',max_fonts:1);
4071
 
print_ln(n_widths:1,' widths of ',max_widths:1,' for ',
4072
 
  n_chars:1,' characters of ',max_chars:1);
4073
 
print_ln(pckt_ptr:1,' byte packets of ',max_packets:1,' with ',
4074
 
  byte_ptr:1,' bytes of ',max_bytes:1);
4075
 
@<Print more memory usage statistics@>@;@/
4076
 
print_ln(stack_used:1,' of ',stack_size:1,' stack and ',
4077
 
  recur_used:1,' of ',max_recursion:1,' recursion levels.')
4078
 
 
4079
 
@ Some implementations may wish to pass the |history| value to the
4080
 
operating system so that it can be used to govern whether or not other
4081
 
programs are started. Here we simply report the history to the user.
4082
 
@^system dependencies@>
4083
 
 
4084
 
@<Print the job |history|@>=
4085
 
case history of
4086
 
spotless: print_ln('(No errors were found.)');
4087
 
harmless_message: print_ln('(Did you see the warning message above?)');
4088
 
error_message: print_ln('(Pardon me, but I think I spotted something wrong.)');
4089
 
fatal_message: print_ln('(That was a fatal error, my friend.)');
4090
 
end {there are no other cases}
4091
 
 
4092
 
@* Low-level output routines.
4093
 
The program uses the binary file variable |out_file| for its main output
4094
 
file; |out_loc| is the number of the byte about to be written next on
4095
 
|out_file|.
4096
 
 
4097
 
@<Glob...@>=
4098
 
@!out_file:byte_file; {the \.{DVI} file we are writing}
4099
 
@!out_loc:int_32; {where we are about to write, in |out_file|}
4100
 
@!out_back:int_32; {a back pointer}
4101
 
@!out_max_v:int_31; {maximum |v| value so far}
4102
 
@!out_max_h:int_31; {maximum |h| value so far}
4103
 
@!out_stack:int_16u; {maximum stack depth}
4104
 
@!out_pages:int_16u; {total number of pages}
4105
 
 
4106
 
@ @<Set ini...@>=
4107
 
out_loc:=0; out_back:=-1;
4108
 
out_max_v:=0; out_max_h:=0;
4109
 
out_stack:=0; out_pages:=0;
4110
 
 
4111
 
@ To prepare |out_file| for output, we |rewrite| it.
4112
 
 
4113
 
@<Open output file(s)@>=
4114
 
rewrite(out_file); {prepares to write packed bytes to |out_file|}
4115
 
 
4116
 
@ For some operating systems it may be necessary to close |out_file|.
4117
 
 
4118
 
@<Close output file(s)@>=
4119
 
 
4120
 
@ Writing the |out_file| should be done as efficient as possible for a
4121
 
particular system; on many systems this means that a large number of
4122
 
bytes will be accumulated in a buffer and is then written from that
4123
 
buffer to |out_file|. In order to simplify such system dependent changes
4124
 
we use the \.{WEB} macro |out_byte| to write the next \.{DVI} byte. Here
4125
 
we give a simple minded definition for this macro in terms of standard
4126
 
\PASCAL.
4127
 
@^system dependencies@>
4128
 
@^optimization@>
4129
 
 
4130
 
@d out_byte(#) == write(out_file,#) {write next \.{DVI} byte}
4131
 
 
4132
 
@ The \.{WEB} macro |out_one| is used to write one byte and to update
4133
 
|out_loc|.
4134
 
 
4135
 
@d out_one(#) == begin out_byte(#); incr(out_loc); @+ end
4136
 
 
4137
 
@ First the |out_packet| procedure copies a packet to |out_file|.
4138
 
 
4139
 
@<Declare typesetting procedures@>=
4140
 
procedure out_packet(@!p:pckt_pointer);
4141
 
var k:byte_pointer; {index into |byte_mem|}
4142
 
begin Incr(out_loc)(pckt_length(p));
4143
 
for k:=pckt_start[p] to pckt_start[p+1]-1 do out_byte(bo(byte_mem[k]));
4144
 
end;
4145
 
 
4146
 
@ Next are the procedures used to write integer numbers or even complete
4147
 
\.{DVI} commands to |out_file|; they all keep |out_loc| up to date.
4148
 
 
4149
 
The |out_four| procedure outputs four bytes in two's complement notation,
4150
 
without risking arithmetic overflow.
4151
 
 
4152
 
@<Declare typesetting procedures@>=
4153
 
procedure out_four(@!x:int_32); {output four bytes}
4154
 
@!begin_four; comp_four(out_byte); Incr(out_loc)(4);
4155
 
end;
4156
 
 
4157
 
@ The |out_char| procedure outputs a |set_char| or \\{set} command or, if
4158
 
|upd=false|, a |put| command.
4159
 
 
4160
 
@<Declare typesetting procedures@>=
4161
 
procedure out_char(@!upd:boolean;@!ext:int_32;@!res:eight_bits);
4162
 
  {output \\{set} or |put|}
4163
 
@!begin_char; comp_char(out_one);
4164
 
end;
4165
 
 
4166
 
@ The |out_unsigned| procedure outputs a |fnt|, |xxx|, or |fnt_def|
4167
 
command with its first parameter (normally unsigned); a |fnt| command
4168
 
is converted into |fnt_num| whenever this is possible.
4169
 
 
4170
 
@<Declare typesetting procedures@>=
4171
 
procedure out_unsigned(@!o:eight_bits;@!x:int_32);
4172
 
  {output |fnt_num|, |fnt|, |xxx|, or |fnt_def|}
4173
 
@!begin_unsigned; comp_unsigned(out_one);
4174
 
end;
4175
 
 
4176
 
@ The |out_signed| procedure outputs a movement (|right|, |w|,
4177
 
|x|, |down|, |y|, or |z|) command with its (signed) parameter.
4178
 
 
4179
 
@<Declare typesetting procedures@>=
4180
 
procedure out_signed(@!o:eight_bits;@!x:int_32);
4181
 
  {output |right|, |w|, |x|, |down|, |y|, or |z|}
4182
 
@!begin_signed; comp_signed(out_one);
4183
 
end;
4184
 
 
4185
 
@ For an output font we set |font_type(f):=out_font_type|; in this case
4186
 
|font_font(f)| is the font number used for font~|f| in |out_file|.
4187
 
@^font types@>
4188
 
 
4189
 
The global variable |out_nf| is the number of fonts already used in
4190
 
|out_file| and the array |out_fnts| contains their internal font numbers;
4191
 
the current font in |out_file| is called |out_fnt|.
4192
 
 
4193
 
@<Glob...@>=
4194
 
@!out_fnts:array[font_number] of font_number; {internal font numbers}
4195
 
@!out_nf:font_number; {number of fonts used in |out_file|}
4196
 
@!out_fnt:font_number; {internal font number of current output font}
4197
 
 
4198
 
@ @<Set init...@>=
4199
 
out_nf:=0;
4200
 
 
4201
 
@ @<Print more font usage statistics@>=
4202
 
print(out_nf:1,' out, ');
4203
 
 
4204
 
@ The |out_fnt_def| procedure outputs a complete font definition
4205
 
command.
4206
 
 
4207
 
@<Declare typesetting procedures@>=
4208
 
procedure out_fnt_def(@!f:font_number);
4209
 
var p:pckt_pointer; {the font name packet}
4210
 
@!k,@!l:byte_pointer; {indices into |byte_mem|}
4211
 
@!a:eight_bits; {length of area part}
4212
 
begin out_unsigned(fnt_def1,font_font(f)); out_four(font_check(f));
4213
 
out_four(font_scaled(f)); out_four(font_design(f));@/
4214
 
p:=font_name(f); k:=pckt_start[p]; l:=pckt_start[p+1]-1;
4215
 
a:=bo(byte_mem[k]);@/
4216
 
Incr(out_loc)(l-k+2); out_byte(a); out_byte(l-k-a);
4217
 
while k<l do
4218
 
  begin incr(k); out_byte(bo(byte_mem[k]));
4219
 
  end;
4220
 
end;
4221
 
 
4222
 
@* Writing the output file.
4223
 
Here we define the device dependent parts of the typesetting routines
4224
 
described earlier in this program.
4225
 
 
4226
 
First we define a few quantities required by the device dependent code
4227
 
for a real output device in order to demonstrate how they might be
4228
 
defined and in order to be able to compile \.{DVIcopy} with the device
4229
 
dependent code included.
4230
 
 
4231
 
@d h_resolution==300 {horizontal resolution in pixels per inch (dpi)}
4232
 
@d v_resolution==300 {vertical resolution in pixels per inch (dpi)}
4233
 
 
4234
 
@d max_h_drift==2 {we insist that |abs(hh-h_pixel_round(h))<=max_h_drift|}
4235
 
@d max_v_drift==2 {we insist that |abs(vv-v_pixel_round(v))<=max_v_drift|}
4236
 
 
4237
 
@<Glob...@>=
4238
 
@!device
4239
 
@!h_conv:real; {converts \.{DVI} units to horizontal pixels}
4240
 
@!v_conv:real; {converts \.{DVI} units to vertical pixels}
4241
 
ecived
4242
 
 
4243
 
@ These are the local variables (if any) needed for |do_pre|.
4244
 
 
4245
 
@<OUT: Declare local variables (if any) for |do_pre|@>=
4246
 
var k:int_15; {general purpose variable}
4247
 
@!p,@!q,@!r:byte_pointer; {indices into |byte_mem|}
4248
 
@!comment:packed array[1..comm_length] of char; {preamble comment prefix}
4249
 
 
4250
 
@ And here is the device dependent code for |do_pre|; the \.{DVI} preamble
4251
 
comment written to |out_file| is similar to the one produced by \.{GFtoPK},
4252
 
but we want to apply our preamble comment prefix only once.
4253
 
 
4254
 
@<OUT: Process the |pre|@>=
4255
 
out_one(pre); out_one(dvi_id);
4256
 
out_four(dvi_num); out_four(dvi_den); out_four(out_mag);@/
4257
 
p:=pckt_start[pckt_ptr-1]; q:=byte_ptr; {location of old \.{DVI} comment}
4258
 
comment:=preamble_comment; pckt_room(comm_length);
4259
 
for k:=1 to comm_length do append_byte(xord[comment[k]]);
4260
 
while byte_mem[p]=bi(" ") do incr(p); {remove leading blanks}
4261
 
if p=q then Decr(byte_ptr)(from_length)
4262
 
else begin k:=0;
4263
 
  while (k<comm_length)and(byte_mem[p+k]=byte_mem[q+k]) do incr(k);
4264
 
  if k=comm_length then Incr(p)(comm_length);
4265
 
  end;
4266
 
k:=byte_ptr-p; {total length}
4267
 
if k>255 then
4268
 
  begin k:=255; q:=p+255-comm_length; {at most 255 bytes}
4269
 
  end;
4270
 
out_one(k); out_packet(new_packet); flush_packet;
4271
 
for r:=p to q-1 do out_one(bo(byte_mem[r]));
4272
 
 
4273
 
@ These are the additional local variables (if any) needed for |do_bop|;
4274
 
the variables |@!i| and |@!j| are already declared.
4275
 
 
4276
 
@<OUT: Declare additional local variables |do_bop|@>=
4277
 
var
4278
 
 
4279
 
@ And here is the device dependent code for |do_bop|.
4280
 
 
4281
 
@<OUT: Process a |bop|@>=
4282
 
out_one(bop); incr(out_pages);
4283
 
for i:=0 to 9 do out_four(count[i]);
4284
 
out_four(out_back); out_back:=out_loc-45;
4285
 
out_fnt:=invalid_font;
4286
 
 
4287
 
@ These are the local variables (if any) needed for |do_eop|.
4288
 
 
4289
 
@<OUT: Declare local variables (if any) for |do_eop|@>=
4290
 
 
4291
 
@ And here is the device dependent code for |do_eop|.
4292
 
 
4293
 
@<OUT: Process an |eop|@>=
4294
 
out_one(eop);
4295
 
 
4296
 
@ These are the local variables (if any) needed for |do_push|.
4297
 
 
4298
 
@<OUT: Declare local variables (if any) for |do_push|@>=
4299
 
 
4300
 
@ And here is the device dependent code for |do_push|.
4301
 
 
4302
 
@<OUT: Process a |push|@>=
4303
 
if stack_ptr>out_stack then out_stack:=stack_ptr;
4304
 
out_one(push);
4305
 
 
4306
 
@ These are the local variables (if any) needed for |do_pop|.
4307
 
 
4308
 
@<OUT: Declare local variables (if any) for |do_pop|@>=
4309
 
 
4310
 
@ And here is the device dependent code for |do_pop|.
4311
 
 
4312
 
@<OUT: Process a |pop|@>=
4313
 
out_one(pop);
4314
 
 
4315
 
@ These are the additional local variables (if any) needed for |do_xxx|;
4316
 
the variable |@!p|, the pointer to the packet containing the special
4317
 
string, is already declared.
4318
 
 
4319
 
@<OUT: Declare additional local variables for |do_xxx|@>=
4320
 
var
4321
 
 
4322
 
@ And here is the device dependent code for |do_xxx|.
4323
 
 
4324
 
@<OUT: Process an |xxx|@>=
4325
 
out_unsigned(xxx1,pckt_length(p)); out_packet(p);
4326
 
 
4327
 
@ These are the local variables (if any) needed for |do_right|.
4328
 
 
4329
 
@<OUT: Declare local variables (if any) for |do_right|@>=
4330
 
 
4331
 
@ And here is the device dependent code for |do_right|.
4332
 
 
4333
 
@<OUT: Process a |right| or |w| or |x|@>=
4334
 
if cur_class<right_cl then out_one(cur_cmd) {|w0| or |x0|}
4335
 
else out_signed(dvi_right_cmd[cur_class],cur_parm); {|right|, |w|, or |x|}
4336
 
 
4337
 
@ Here we update the |out_max_h| value.
4338
 
 
4339
 
@<OUT: Move right@>=
4340
 
if abs(cur_h)>out_max_h then out_max_h:=abs(cur_h);
4341
 
 
4342
 
@ These are the local variables (if any) needed for |do_down|.
4343
 
 
4344
 
@<OUT: Declare local variables (if any) for |do_down|@>=
4345
 
 
4346
 
@ And here is the device dependent code for |do_down|.
4347
 
 
4348
 
@<OUT: Process a |down| or |y| or |z|@>=
4349
 
if cur_class<down_cl then out_one(cur_cmd) {|y0| or |z0|}
4350
 
else out_signed(dvi_down_cmd[cur_class],cur_parm); {|down|, |y|, or |z|}
4351
 
 
4352
 
@ Here we update the |out_max_v| value.
4353
 
 
4354
 
@<OUT: Move down@>=
4355
 
if abs(cur_v)>out_max_v then out_max_v:=abs(cur_v);
4356
 
 
4357
 
@ These are the local variables (if any) needed for |do_width|.
4358
 
 
4359
 
@<OUT: Declare local variables (if any) for |do_width|@>=
4360
 
 
4361
 
@ And here is the device dependent code for |do_width|.
4362
 
 
4363
 
@<OUT: Typeset a |width|@>=
4364
 
out_one(set_rule);
4365
 
out_four(width_dimen); out_four(cur_h_dimen);
4366
 
 
4367
 
@ These are the additional local variables (if any) needed for |do_rule|;
4368
 
the variable |@!visible| is already declared.
4369
 
 
4370
 
@<OUT: Declare additional local variables |do_rule|@>=
4371
 
var
4372
 
 
4373
 
@ And here is the device dependent code for |do_rule|.
4374
 
 
4375
 
@<OUT: Typeset a visible |rule|@>=
4376
 
out_one(dvi_rule_cmd[cur_upd]);
4377
 
out_four(cur_v_dimen); out_four(cur_h_dimen);
4378
 
 
4379
 
@ @<OUT: Typeset an invisible |rule|@>=
4380
 
@<OUT: Typeset a visible |rule|@>
4381
 
 
4382
 
@ These are the additional local variables (if any) needed for |do_font|;
4383
 
the variable |@!p| is already declared.
4384
 
 
4385
 
@<OUT: Declare additional local variables for |do_font|@>=
4386
 
var
4387
 
 
4388
 
@ And here is the device dependent code for |do_font|; if the \.{VF} file
4389
 
for a font could not be found, we simply assume this must be a real font.
4390
 
 
4391
 
@<OUT: Look for a font file before trying to read the \.{VF} file;
4392
 
  if found |goto done|@>=
4393
 
 
4394
 
@ @<OUT: Look for a font file after trying to read the \.{VF} file@>=
4395
 
if(out_nf>=max_fonts) then overflow(str_fonts,max_fonts);
4396
 
print('OUT: font ',cur_fnt:1); d_print(' => ',out_nf:1);
4397
 
print_font(cur_fnt);
4398
 
d_print(' at ',font_scaled(cur_fnt):1,' DVI units'); print_ln('.');
4399
 
font_type(cur_fnt):=out_font_type; font_font(cur_fnt):=out_nf;
4400
 
out_fnts[out_nf]:=cur_fnt; incr(out_nf);
4401
 
out_fnt_def(cur_fnt);
4402
 
 
4403
 
@ And here is some device dependent code used before each character.
4404
 
 
4405
 
@<OUT: Prepare to use font |cur_fnt|@>=
4406
 
 
4407
 
@ These are the local variables (if any) needed for |do_char|.
4408
 
 
4409
 
@<OUT: Declare local variables (if any) for |do_char|@>=
4410
 
 
4411
 
@ And here is the device dependent code for |do_char|.
4412
 
 
4413
 
@<OUT: Typeset a |char|@>=
4414
 
@!debug if font_type(cur_fnt)<>out_font_type then confusion(str_fonts);
4415
 
gubed @;
4416
 
if cur_fnt<>out_fnt then
4417
 
  begin out_unsigned(fnt1,font_font(cur_fnt)); out_fnt:=cur_fnt;
4418
 
  end;
4419
 
out_char(cur_upd,cur_ext,cur_res);
4420
 
 
4421
 
@ If the program terminates in the middle of a page, we write as many
4422
 
|pop|s as necessary and one |eop|.
4423
 
 
4424
 
@<OUT: Finish incomplete page@>=
4425
 
begin while stack_ptr>0 do
4426
 
  begin out_one(pop); decr(stack_ptr);
4427
 
  end;
4428
 
  out_one(eop);
4429
 
end
4430
 
 
4431
 
@ If the output file has been started, we write the postamble; in
4432
 
addition we print the number of bytes and pages written to |out_file|.
4433
 
 
4434
 
@<OUT: Finish output file(s)@>=
4435
 
if out_loc>0 then
4436
 
  begin @<OUT: Write the postamble@>;
4437
 
  k:=7-((out_loc-1) mod 4); {the number of |dvi_pad| bytes}
4438
 
  while k>0 do
4439
 
    begin out_one(dvi_pad); decr(k);
4440
 
    end;
4441
 
  print('OUT file: ',out_loc:1,' bytes, ',out_pages:1,' page');
4442
 
  if out_pages<>1 then print('s');
4443
 
  end
4444
 
else print('OUT file: no output');
4445
 
print_ln(' written.');
4446
 
if out_pages=0 then mark_harmless;
4447
 
 
4448
 
@ Here we simply write the values accumulated during the \.{DVI} output.
4449
 
 
4450
 
@<OUT: Write the postamble@>=
4451
 
out_one(post); out_four(out_back); out_back:=out_loc-5;@/
4452
 
out_four(dvi_num); out_four(dvi_den); out_four(out_mag);@/
4453
 
out_four(out_max_v); out_four(out_max_h);@/
4454
 
out_one(out_stack div @"100); out_one(out_stack mod @"100);@/
4455
 
out_one(out_pages div @"100); out_one(out_pages mod @"100);@/
4456
 
k:=out_nf;
4457
 
while k>0 do
4458
 
  begin decr(k); out_fnt_def(out_fnts[k]);
4459
 
  end;
4460
 
out_one(post_post); out_four(out_back);@/
4461
 
out_one(dvi_id)
4462
 
 
4463
 
@ Here we could print more memory usage statistics; this possibility is,
4464
 
however, not used for \.{DVIcopy}.
4465
 
 
4466
 
@<Print more memory usage statistics@>=
4467
 
 
4468
 
@* System-dependent changes.
4469
 
This section should be replaced, if necessary, by changes to the program
4470
 
that are necessary to make \.{DVIcopy} work at a particular installation.
4471
 
It is usually best to design your change file so that all changes to
4472
 
previous sections preserve the section numbering; then everybody's version
4473
 
will be consistent with the printed program. More extensive changes,
4474
 
which introduce new sections, can be inserted here; then only the index
4475
 
itself will get a new section number.
4476
 
@^system dependencies@>
4477
 
 
4478
 
@* Index.
4479
 
Pointers to error messages appear here together with the section numbers
4480
 
where each ident\-i\-fier is used.