diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /dviware/dviimp/dviimp.web |
Initial commit
Diffstat (limited to 'dviware/dviimp/dviimp.web')
-rw-r--r-- | dviware/dviimp/dviimp.web | 4261 |
1 files changed, 4261 insertions, 0 deletions
diff --git a/dviware/dviimp/dviimp.web b/dviware/dviimp/dviimp.web new file mode 100644 index 0000000000..5e5c5c803e --- /dev/null +++ b/dviware/dviimp/dviimp.web @@ -0,0 +1,4261 @@ +% This program by A. L. Samuel is not copyrighted and can be used freely. +% This program depends heavily on DVItype.WEB by D. E. Knuth for much of +% the basic material relating to the reading of DVI files and on GFtoDOVER +% for much of the basic material relating to the reading of GF files. +% The idea of getting the font information directly from the GF files +% rather than from PXL and TFM files was suggested by D. E. Knuth, +% Several people have contributed ideas as to fast methods of doing this. + +% Version 0.3 now accepts as many as 50 256-character fonts and it does an +% automatic spooling job for the Imagen with the pages properly collated. +% Version 0.4 Corrections for the new_row_69 bug and a major clean-up by +% D.R.Fuchs with the introduction of |debug| and |gubed| instead of the +% earlier temporary fix. +% Version 0.5 Fix to get TFM widths for fonts with no GF file available. +% Version 0.6 Fix to handle |empty_glyph| cases properly, and a minor +% change to the |reconcile_scale| routine. +% Version 0.7 Major change to |m_store| now |mm_store|, making it to +% store from |[0,4] through |[0,85999]| then to |[1,4]| through |[1,85999]|. +% Version 0.8 Added switches /f, /n, and /c, being respectively, the number +% count[0] of the first page to be printed, the total number of pages and the +% number of copies desired. +% Version 0.9 Added xxx{point <number>} and xxx{join <pen size> number1> +% <number2>... special commands to locate points and draw lines. +% Also improved /f and /n to allow for Roman and Arabic page number mixes. +% Version 0.91 Added Imagen's version of circ_arc and ellipse_arc and made +% several very minor bug fixes. +% Version 0.92 Added on-line disagreement reports for check_sum, design_size, +% and at-size. Also deleted a number of unneeded variables and cleaned things +% up a bit. +% Version 0.93 Fixed some off-by-one bugs in indexing the |mm_store| array. +% (JJW) +% Version 0.94 Fixed tfm loading ala TeX 2.7. (TGR) + +% Here is TeX material that gets inserted after \input webmac +\def\hang{\hangindent 3em\indent\ignorespaces} +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps for names like PASCAL +\def\PASCAL{{\mc PASCAL}} +\let\swap=\leftrightarrow +\font\logo=logo10 % font used for the METAFONT logo +\def\MF{{\logo META}\-{\logo FONT}} + +\def\(#1){} % this is used to make section names sort themselves better +\def\9#1{} % this is used for sort keys in the index + +\def\title{DVIIMP} +\def\contentspagenumber{1} +\def\topofcontents{\null + \def\titlepage{F} % include headline on the contents page + \def\rheader{\mainfont\hfil \contentspagenumber} + \vfill + \centerline{\titlefont The {\ttitlefont DVIIMP} processor} + \vskip 15pt + \centerline{(Version 0.94, November 1987)} + \vfill} +\def\botofcontents{\vfill + \centerline{\hsize 5in\baselineskip9pt + \vbox{\ninerm\noindent + The preparation of this report + was supported in part by the National Science + Foundation under grants IST-8201926 and MCS-8300984, + and by the System Development Foundation. `\TeX' is a + trademark of the American Mathematical Society.}}} +\pageno=\contentspagenumber \advance\pageno by 1 + +@* Introduction. + +This \.{DVIIMP} program reads binary device-independent (``\.{DVI}'') +files that are produced by document compilers such as \TeX, and converts +them into a form acceptable to the \.{IMAGEN} printer. The primary use of +this program will be to print documents that use a large variety of +different fonts that are freshly prepared by the \MF\ program and with +this use in mind the program gets the needed font information directly +from \.{GF} files. This direct use of \.{GF} font information may set a +trend but it should be noted that many older but still useful fonts may +not be available in \.{GF} form. \.{DVIIMP} has been written in the +\.{WEB} language to conform with the general practice for other programs +of this general type and to simplify the task of adapting it for use on a +variety of different computers and different operating systems. + +This program reads the \.{GF} files and stores the font information +(somewhat compressed and simplified from the \.{GF} file format) in an +array called |mm_store|, and only translates the detailed raster +information into the needed \.{imPRESS} format a glyph at a time on the first +occurence of each needed glyph in the document being translated. This +requires a rather involved procedure for keeping a record of those glyphs +that have already been transmitted and of providing for the possibilities +that the memory space allowed for fonts in the main memory associated with +this program and the internal memory within the \.{IMAGEN} for glyphs may +not be large enough for the job without arranging for the deletion of some +font information and its possible replacement should it again prove to be +needed. + +There seems to be a 2-to-the-17th-pixel limit to the maximum permitted +size of glyph that IMAGEN will accept, measured as the product of the +glyph's width (rounded up to a whole number of bytes) and its height. + +The |banner| string defined here should be changed whenever \.{DVIIMP} +gets modified. + +@d banner=='This is DVIIMP, Version 0.94' {printed when the program starts} +@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} +@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} +@f debug==begin +@f gubed==end + +@ This program is written in standard \PASCAL, except where it is necessary +to use extensions; for example, \.{DVIIMP} must read files whose names +are dynamically specified, and that would be impossible in pure \PASCAL. +All places where nonstandard constructions are used have been listed in +the index under ``system dependencies.'' +@!@^system dependencies@> + +One of the extensions to standard \PASCAL\ that we shall deal with is the +ability to move to a random place in a binary file; another is to +determine the length of a binary file. If \.{DVIIMP} is being used +with \PASCAL s for which random file positioning is not efficiently +available, the following definition should be changed from |true| to +|false|; in such cases, \.{DVIIMP} will not include the optional feature +that reads the postamble first. + +Another extension is to use a default |case| as in \.{TANGLE}, \.{WEAVE}, +etc. + +@d random_reading==true {should we skip around in the file?} +@d othercases == others: {default for cases not listed explicitly} +@d endcases == @+end {follows the default case in an extended |case| statement} +@f othercases == else +@f endcases == end + +@ The binary input comes from |dvi_file|, and the symbolic output is written +on \PASCAL's standard |output| file. The term |print| is used instead of +|write| when this program writes on |output|, so that all such output +could easily be redirected if desired. + +@d print(#)==write(#) +@d print_ln(#)==write_ln(#) +@d print_nl==write_ln + +@p program DVI_IMP(@!dvi_file,@!im_file,@!output); +label @<Labels in the outer block@>@/ +const @<Constants in the outer block@>@/ +type @<Types in the outer block@>@/ +var @<Globals in the outer block@>@/ +procedure initialize; {this procedure gets things started properly} + var i:integer; {loop index for initializations} + jj:real; {a real variable} + begin print_ln(banner);@/ + @<Set initial values@>@/ + end; + +@ If the program has to stop prematurely, it goes to the +`|final_end|'. Another label, |done|, is used when stopping normally. + +@d final_end=9999 {label for the end of it all} +@d done=30 {go here when finished with a subtask} +@d restart=40 {go here to restart an operation} + +@<Labels...@>=final_end; + +@ The following parameters can be changed at compile time to extend or +reduce \.{DVIIMP}'s capacity. + +@<Constants...@>= +@!max_fonts=100; {maximum number of distinct fonts per \.{DVI} file} +@!max_glyphs=7680; {maximum number of different characters among all fonts} +@!line_length=320; {bracketed lines of output will be at most this long} +@!terminal_line_length=150; {maximum number of characters input in a single + line of input from the terminal} +@!stack_size=200; {\.{DVI} files shouldn't |push| beyond this depth} +@!name_size=1000; {total length of all font file names} +@!name_length=50; {a file name shouldn't be longer than this} +@!m1_max=3; {max first |mm_store| index} +@!m2_size=86000; {used as multiplier or divider} +@!m2_max= 85999; {max second |mm_store| index} +@!mm_size=344000; {bytes in |mm_store|} +@!mm_max= 343999; {max location in |mm_store|} +@!max_char_no=255; {largest allowed char number} + +@ Here are some macros for common programming idioms. We will have occasion, +both in the |do_page| and the |do_char| routines, to group certain cases +together and so we will also define these groupings at this time. + +@d incr(#) == #:=#+1 {increase a variable by unity} +@d decr(#) == #:=#-1 {decrease a variable by unity} +@d do_nothing == {empty statement} +@d unity == @'200000 {$2^{16}$, represents 1.00000} +@d three_cases(#)==#,#+1,#+2 +@d four_cases(#)==#,#+1,#+2,#+3 +@d eight_cases(#)==four_cases(#),four_cases(#+4) +@d nine_cases(#)==eight_cases(#),#+8 +@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8) +@d nineteen_cases(#)==nine_cases(#),nine_cases(#+9),#+18 +@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16) +@d thirty_seven_cases(#)==thirty_two_cases(#),four_cases(#+32),#+36 +@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32) +@d eighty_three_cases(#)==sixty_four_cases(#),nineteen_cases(#+64) +@d one_sixty_five_cases(#)== + sixty_four_cases(#), sixty_four_cases(#+64), + thirty_seven_cases(#+128) + +@ If the \.{DVI} file is badly malformed, the whole process must be aborted; +\.{DVIIMP} will give up, after issuing an error message about the symptoms +that were noticed. + +Such errors might be discovered inside of subroutines inside of subroutines, +so a procedure called |jump_out| has been introduced. This procedure, which +simply transfers control to the label |final_end| at the end of the program, +contains the only non-local |goto| statement in \.{DVIIMP}. +@^system dependencies@> + +@d abort(#)==begin print(' ',#); jump_out; + end +@d bad_dvi(#)==abort('Bad DVI file: ',#,'!') +@.Bad DVI file@> + +@p procedure jump_out; +begin goto final_end; +end; + +@* The character set. +Like all programs written with the \.{WEB} system, \.{DVIIMP} can be +used with any character set. But it uses ASCII code internally, because +the programming for portable input-output is easier when a fixed internal +code is used, and because \.{DVI} files use ASCII code for file names +and certain other strings. + +The next few sections of \.{DVIIMP} have therefore been copied from the +analogous ones in the \.{WEB} system routines. They have been considerably +simplified, since \.{DVIIMP} need not deal with the controversial +ASCII codes less than @'40. If such codes appear in the \.{DVI} file, +they will be printed as question marks. + +@<Types...@>= +@!ASCII_code=" ".."~"; {a subrange of the integers} + +@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit +character sets were common, so it did not make provision for lower case +letters. Nowadays, of course, we need to deal with both upper and lower case +alphabets in a convenient way, especially in a program like \.{DVIIMP}. +So we shall assume that the \PASCAL\ system being used for \.{DVIIMP} +has a character set containing at least the standard visible characters +of ASCII code (|"!"| through |"~"|). + +Some \PASCAL\ compilers use the original name |char| for the data type +associated with the characters in text files, while other \PASCAL s +consider |char| to be a 64-element subrange of a larger data type that has +some other name. In order to accommodate this difference, we shall use +the name |text_char| to stand for the data type of the characters in the +output file. We shall also assume that |text_char| consists of +the elements |chr(first_text_char)| through |chr(last_text_char)|, +inclusive. The following definitions should be adjusted if necessary. +@^system dependencies@> + +@d text_char == char {the data type of characters in text files} +@d first_text_char=0 {ordinal number of the smallest element of |text_char|} +@d last_text_char=127 {ordinal number of the largest element of |text_char|} + +@<Types...@>= +@!text_file=packed file of text_char; + +@ The \.{DVIIMP} processor converts between ASCII code and +the user's external character set by means of arrays |xord| and |xchr| +that are analogous to \PASCAL's |ord| and |chr| functions. + +@<Globals...@>= +@!xord: array [text_char] of ASCII_code; + {specifies conversion of input characters} +@!xchr: array [0..255] of text_char; + {specifies conversion of output characters} + +@ Under our assumption that the visible characters of standard ASCII are +all present, the following assignment statements initialize the +|xchr| array properly, without needing any system-dependent changes. + +@<Set init...@>= +for i:=0 to @'37 do xchr[i]:='?'; +xchr[@'40]:=' '; +xchr[@'41]:='!'; +xchr[@'42]:='"'; +xchr[@'43]:='#'; +xchr[@'44]:='$'; +xchr[@'45]:='%'; +xchr[@'46]:='&'; +xchr[@'47]:='''';@/ +xchr[@'50]:='('; +xchr[@'51]:=')'; +xchr[@'52]:='*'; +xchr[@'53]:='+'; +xchr[@'54]:=','; +xchr[@'55]:='-'; +xchr[@'56]:='.'; +xchr[@'57]:='/';@/ +xchr[@'60]:='0'; +xchr[@'61]:='1'; +xchr[@'62]:='2'; +xchr[@'63]:='3'; +xchr[@'64]:='4'; +xchr[@'65]:='5'; +xchr[@'66]:='6'; +xchr[@'67]:='7';@/ +xchr[@'70]:='8'; +xchr[@'71]:='9'; +xchr[@'72]:=':'; +xchr[@'73]:=';'; +xchr[@'74]:='<'; +xchr[@'75]:='='; +xchr[@'76]:='>'; +xchr[@'77]:='?';@/ +xchr[@'100]:='@@'; +xchr[@'101]:='A'; +xchr[@'102]:='B'; +xchr[@'103]:='C'; +xchr[@'104]:='D'; +xchr[@'105]:='E'; +xchr[@'106]:='F'; +xchr[@'107]:='G';@/ +xchr[@'110]:='H'; +xchr[@'111]:='I'; +xchr[@'112]:='J'; +xchr[@'113]:='K'; +xchr[@'114]:='L'; +xchr[@'115]:='M'; +xchr[@'116]:='N'; +xchr[@'117]:='O';@/ +xchr[@'120]:='P'; +xchr[@'121]:='Q'; +xchr[@'122]:='R'; +xchr[@'123]:='S'; +xchr[@'124]:='T'; +xchr[@'125]:='U'; +xchr[@'126]:='V'; +xchr[@'127]:='W';@/ +xchr[@'130]:='X'; +xchr[@'131]:='Y'; +xchr[@'132]:='Z'; +xchr[@'133]:='['; +xchr[@'134]:='\'; +xchr[@'135]:=']'; +xchr[@'136]:='^'; +xchr[@'137]:='_';@/ +xchr[@'140]:='`'; +xchr[@'141]:='a'; +xchr[@'142]:='b'; +xchr[@'143]:='c'; +xchr[@'144]:='d'; +xchr[@'145]:='e'; +xchr[@'146]:='f'; +xchr[@'147]:='g';@/ +xchr[@'150]:='h'; +xchr[@'151]:='i'; +xchr[@'152]:='j'; +xchr[@'153]:='k'; +xchr[@'154]:='l'; +xchr[@'155]:='m'; +xchr[@'156]:='n'; +xchr[@'157]:='o';@/ +xchr[@'160]:='p'; +xchr[@'161]:='q'; +xchr[@'162]:='r'; +xchr[@'163]:='s'; +xchr[@'164]:='t'; +xchr[@'165]:='u'; +xchr[@'166]:='v'; +xchr[@'167]:='w';@/ +xchr[@'170]:='x'; +xchr[@'171]:='y'; +xchr[@'172]:='z'; +xchr[@'173]:='{'; +xchr[@'174]:='|'; +xchr[@'175]:='}'; +xchr[@'176]:='~'; +for i:=@'177 to 255 do xchr[i]:='?'; + +@ The following system-independent code makes the |xord| array contain a +suitable inverse to the information in |xchr|. + +@<Set init...@>= +for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40; +for i:=" " to "~" do xord[xchr[i]]:=i; + +@* Device-independent file format. +Before we get into the details of \.{DVIIMP}, we need to know exactly +what \.{DVI} files are. The form of such files was designed by David R. +@^Fuchs, David Raymond@> +Fuchs in 1979. Almost any reasonable typesetting device can be driven by +a program that takes \.{DVI} files as input, and dozens of such +\.{DVI}-to-whatever programs have been written. Thus, it is possible to +print the output of document compilers like \TeX\ on many different kinds +of equipment. + +A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a +series of commands in a machine-like language. The first byte of each command +is the operation code, and this code is followed by zero or more bytes +that provide parameters to the command. The parameters themselves may consist +of several consecutive bytes; for example, the `|set_rule|' command has two +parameters, each of which is four bytes long. Parameters are usually +regarded as nonnegative integers; but four-byte-long parameters, +and shorter parameters that denote distances, can be +either positive or negative. Such parameters are given in two's complement +notation. For example, a two-byte-long distance parameter has a value between +$-2^{15}$ and $2^{15}-1$. +@.DVI {\rm files}@> + +A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one +or more ``pages,'' followed by a ``postamble.'' The preamble is simply a +|pre| command, with its parameters that define the dimensions used in the +file; this must come first. Each ``page'' consists of a |bop| command, +followed by any number of other commands that tell where characters are to +be placed on a physical page, followed by an |eop| command. The pages +appear in the order that they were generated, not in any particular +numerical order. If we ignore |nop| commands and \\{fnt\_def} commands +(which are allowed between any two commands in the file), each |eop| +command is immediately followed by a |bop| command, or by a |post| +command; in the latter case, there are no more pages in the file, and the +remaining bytes form the postamble. Further details about the postamble +will be explained later. + +Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte +quantities that give the location number of some other byte in the file; +the first byte is number~0, then comes number~1, and so on. For example, +one of the parameters of a |bop| command points to the previous |bop|; +this makes it feasible to read the pages in backwards order, in case the +results are being directed to a device that stacks its output face up. +Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the +first page occupies bytes 100 to 999, say, and if the second +page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000 +points to 100 and the |bop| that starts in byte 2000 points to 1000. (The +very first |bop|, i.e., the one that starts in byte 100, has a pointer of $-1$.) + +@ The \.{DVI} format is intended to be both compact and easily interpreted +by a machine. Compactness is achieved by making most of the information +implicit instead of explicit. When a \.{DVI}-reading program reads the +commands for a page, it keeps track of several quantities: (a)~The current +font |f| is an integer; this value is changed only +by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page +is given by two numbers called the horizontal and vertical coordinates, +|h| and |v|. Both coordinates are zero at the upper left corner of the page; +moving to the right corresponds to increasing the horizontal coordinate, and +moving down corresponds to increasing the vertical coordinate. Thus, the +coordinates are essentially Cartesian, except that vertical directions are +flipped; the Cartesian version of |(h,v)| would be |(h,-v)|. (c)~The +current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|, +where |w| and~|x| are used for horizontal spacing and where |y| and~|z| +are used for vertical spacing. (d)~There is a stack containing +|(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to +change the current level of operation. Note that the current font~|f| is +not pushed and popped; the stack contains only information about +positioning. + +The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up +to 32 bits, including the sign. Since they represent physical distances, +there is a small unit of measurement such that increasing |h| by~1 means +moving a certain tiny distance to the right. The actual unit of +measurement is variable, as explained below. + +@ Here is a list of all the commands that may appear in a \.{DVI} file. Each +command is specified by its symbolic name (e.g., |bop|), its opcode byte +(e.g., 139), and its parameters (if any). The parameters are followed +by a bracketed number telling how many bytes they occupy; for example, +`|p[4]|' means that parameter |p| is four bytes long. (A somewhat +similar set of commands is used in \.{GF} files, as will be +explained in a later section). + +\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f| +such that the reference point of the character is at |(h,v)|. Then +increase |h| by the width of that character. Note that a character may +have zero or negative width, so one cannot be sure that |h| will advance +after this command; but |h| usually does increase. + +\yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127). +Do the operations of |set_char_0|; but use the character whose number +matches the opcode, instead of character~0. + +\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character +number~|c| is typeset. \TeX82 uses this command for characters in the +range |128<=c<256|. + +\yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that |c|~is two +bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this +command, which is intended for processors that deal with oriental languages; +but \.{DVIIMP} will allow character codes greater than 255, assuming that +they all have the same width as the character whose code is $c \bmod 256$. +@^oriental characters@>@^Chinese characters@>@^Japanese characters@> + +\yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that |c|~is three +bytes long, so it can be as large as $2^{24}-1$. + +\yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that |c|~is four +bytes long, possibly even negative. Imagine that. + +\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle +of height |a| and width |b|, with its bottom left corner at |(h,v)|. Then +set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note +that if |b<0|, the value of |h| will decrease even though nothing else happens. +Programs that typeset from \.{DVI} files should be careful to make the rules +line up carefully with digitized characters, as explained in connection with +the |rule_pixels| subroutine below. + +\yskip\hang|put1| 133 |c[1]|. Typeset character number~|c| from font~|f| +such that the reference point of the character is at |(h,v)|. (The `put' +commands are exactly like the `set' commands, except that they simply put out a +character or a rule without moving the reference point afterwards.) + +\yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed. + +\yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed. + +\yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed. + +\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that +|h| is not changed. + +\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s +may occur between \.{DVI} commands, but a |nop| cannot be inserted between +a command and its parameters or between two parameters. + +\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning +of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set +the current font |f| to an undefined value. The ten $c_i$ parameters can +be used to identify pages, if a user wants to print only part of a \.{DVI} +file; \TeX82 gives them the values of \.{\\count0} $\ldots$ \.{\\count9} +at the time \.{\\shipout} was invoked for this page. The parameter |p| +points to the previous |bop| command in the file, where the first |bop| +has $p=-1$. + +\yskip\hang|eop| 140. End of page: Print what you have read since the +previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading +programs that drive most output devices will have kept a buffer of the +material that appears on the page that has just ended. This material is +largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by +|h|~coordinate; so it usually needs to be sorted into some order that is +appropriate for the device in question. \.{DVIIMP} does not do such sorting.) + +\yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the +top of the stack; do not change any of these values. Note that |f| is +not pushed. + +\yskip\hang|pop| 142. Pop the top six values off of the stack and assign +them to |(h,v,w,x,y,z)|. The number of pops should never exceed the number +of pushes, since it would be highly embarrassing if the stack were empty +at the time of a |pop| command. + +\yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units. +The parameter is a signed number in two's complement notation, |-128<=b<128|; +if |b<0|, the reference point actually moves left. + +\yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a +two-byte quantity in the range |-32768<=b<32768|. + +\yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a +three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|. + +\yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a +four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|. + +\yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck, +this parameterless command will usually suffice, because the same kind of motion +will occur several times in succession; the following commands explain how +|w| gets particular values. + +\yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a +signed quantity in two's complement notation, |-128<=b<128|. This command +changes the current |w|~spacing and moves right by |b|. + +\yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is a two-byte-long +parameter, |-32768<=b<32768|. + +\yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is a three-byte-long +parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|. + +\yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is a four-byte-long +parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|. + +\yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|' +commands are like the `|w|' commands except that they involve |x| instead +of |w|. + +\yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a +signed quantity in two's complement notation, |-128<=b<128|. This command +changes the current |x|~spacing and moves right by |b|. + +\yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is a two-byte-long +parameter, |-32768<=b<32768|. + +\yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is a three-byte-long +parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|. + +\yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is a four-byte-long +parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|. + +\yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units. +The parameter is a signed number in two's complement notation, |-128<=a<128|; +if |a<0|, the reference point actually moves up. + +\yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a +two-byte quantity in the range |-32768<=a<32768|. + +\yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a +three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|. + +\yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a +four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|. + +\yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck, +this parameterless command will usually suffice, because the same kind of motion +will occur several times in succession; the following commands explain how +|y| gets particular values. + +\yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a +signed quantity in two's complement notation, |-128<=a<128|. This command +changes the current |y|~spacing and moves down by |a|. + +\yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is a two-byte-long +parameter, |-32768<=a<32768|. + +\yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is a three-byte-long +parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|. + +\yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is a four-byte-long +parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|. + +\yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands +are like the `|y|' commands except that they involve |z| instead of |y|. + +\yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a +signed quantity in two's complement notation, |-128<=a<128|. This command +changes the current |z|~spacing and moves down by |a|. + +\yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is a two-byte-long +parameter, |-32768<=a<32768|. + +\yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is a three-byte-long +parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|. + +\yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is a four-byte-long +parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|. + +\yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been +defined by a \\{fnt\_def} instruction, as explained below. + +\yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set +|f:=1|, \dots, |f:=63|, respectively. + +\yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font +numbers in the range |64<=k<256|. + +\yskip\hang|fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two +bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this +command, but large font numbers may prove useful for specifications of +color or texture, or they may be used for special fonts that have fixed +numbers in some external coding scheme. + +\yskip\hang|fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three +bytes long, so it can be as large as $2^{24}-1$. + +\yskip\hang|fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four +bytes long; this is for the really big font numbers (and for the negative ones). + +\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in +general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading +programs are being used. \TeX82 generates |xxx1| when a short enough +\.{\\special} appears, setting |k| to the number of bytes being sent. It +is recommended that |x| be a string having the form of a keyword followed +by possible parameters relevant to that keyword. + +\yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|. + +\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|. + +\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously +large. \TeX82 uses |xxx4| when |xxx1| would be incorrect. + +\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |0<=k<256|; font definitions will be explained shortly. + +\yskip\hang|fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |0<=k<65536|. + +\yskip\hang|fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |0<=k<@t$2^{24}$@>|. + +\yskip\hang|fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|. + +\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|. +Beginning of the preamble; this must come at the very beginning of the +file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below. + +\yskip\hang|post| 248. Beginning of the postamble, see below. + +\yskip\hang|post_post| 249. Ending of the postamble, see below. + +\yskip\noindent Commands 250--255 are undefined at the present time. + +@ @d set_char_0=0 {typeset character 0 and move right} +@d set1=128 {typeset a character and move right} +@d set_rule=132 {typeset a rule and move right} +@d put1=133 {typeset a character} +@d put_rule=137 {typeset a rule} +@d nop=138 {no operation} +@d bop=139 {beginning of page} +@d eop=140 {ending of page} +@d push=141 {save the current positions} +@d pop=142 {restore previous positions} +@d right1=143 {move right} +@d w0=147 {move right by |w|} +@d w1=148 {move right and set |w|} +@d x0=152 {move right by |x|} +@d x1=153 {move right and set |x|} +@d down1=157 {move down} +@d y0=161 {move down by |y|} +@d y1=162 {move down and set |y|} +@d z0=166 {move down by |z|} +@d z1=167 {move down and set |z|} +@d fnt_num_0=171 {set current font to 0} +@d fnt1=235 {set current font} +@d xxx1=239 {extension to \.{DVI} primitives} +@d xxx4=242 {potentially long extension to \.{DVI} primitives} +@d fnt_def1=243 {define the meaning of a font number} +@d pre=247 {preamble} +@d post=248 {postamble beginning} +@d post_post=249 {postamble ending} +@d undefined_commands==250,251,252,253,254,255 + +@ The preamble contains basic information about the file as a whole. As +stated above, there are six parameters: +$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$ +The |i| byte identifies \.{DVI} format; currently this byte is always set +to~2. (Some day we will set |i=3|, when \.{DVI} format makes another +incompatible change---perhaps in 1992.) + +The next two parameters, |num| and |den|, are positive integers that define +the units of measurement; they are the numerator and denominator of a +fraction by which all dimensions in the \.{DVI} file could be multiplied +in order to get lengths in units of $10^{-7}$ meters. (For example, there are +exactly 7227 \TeX\ points in 254 centimeters, and \TeX82 works with scaled +points where there are $2^{16}$ sp in a point, so \TeX82 sets |num=25400000| +and $|den|=7227\cdot2^{16}=473628672$.) +@^sp@> + +The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the +desired magnification. The actual fraction by which dimensions are +multiplied is therefore $mn/1000d$. Note that if a \TeX\ source document +does not call for any `\.{true}' dimensions, and if you change it only by +specifying a different \.{\\mag} setting, the \.{DVI} file that \TeX\ +creates will be completely unchanged except for the value of |mag| in the +preamble and postamble. (Fancy \.{DVI}-reading programs allow users to +override the |mag|~setting when a \.{DVI} file is being printed.) + +Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not +interpreted further. The length of comment |x| is |k|, where |0<=k<256|. + +@d id_byte=2 {identifies the kind of \.{DVI} files described here} + +@ Font definitions for a given font number |k| contain further parameters +$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$ +The four-byte value |c| is the check sum that \TeX\ (or whatever program +generated the \.{DVI} file) found in the \.{GF} file for this font; +|c| should match the check sum of the font found by programs that read +this \.{DVI} file. +@^check sum@> + +Parameter |s| contains a fixed-point scale factor that is applied to the +character widths in font |k|; font dimensions in \.{GF} files and other +font files are relative to this quantity, which is always positive and +less than $2^{27}$. It is given in the same units as the other dimensions +of the \.{DVI} file. Parameter |d| is similar to |s|; it is the ``design +size,'' and it is given in \.{DVI} units that have not been corrected for +the magnification~|mag| found in the preamble. Thus, font |k| is to be +used at $|mag|\cdot s/1000d$ times its normal size. + +The remaining part of a font definition gives the external name of the font, +which is an ASCII string of length |a+l|. The number |a| is the length +of the ``area'' or directory, and |l| is the length of the font name itself; +the standard local system font area is supposed to be used when |a=0|. +The |n| field contains the area in its first |a| bytes. + +Font definitions must appear before the first use of a particular font number. +Once font |k| is defined, it must not be defined again; however, we +shall see below that font definitions appear in the postamble as well as +in the pages, so in this sense each font number is defined exactly twice, +if at all. Like |nop| commands and \\{xxx} commands, font definitions can +appear before the first |bop|, or between an |eop| and a |bop|. + +@ The last page in a \.{DVI} file is followed by `|post|'; this command +introduces the postamble, which summarizes important facts that \TeX\ has +accumulated about the file, making it possible to print subsets of the data +with reasonable efficiency. The postamble has the form +$$\vbox{\halign{\hbox{#\hfil}\cr + |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr + $\langle\,$font definitions$\,\rangle$\cr + |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$ +Here |p| is a pointer to the final |bop| in the file. The next three +parameters, |num|, |den|, and |mag|, are duplicates of the quantities that +appeared in the preamble. + +Parameters |l| and |u| give respectively the height-plus-depth of the tallest +page and the width of the widest page, in the same units as other dimensions +of the file. These numbers might be used by a \.{DVI}-reading program to +position individual ``pages'' on large sheets of film or paper. + +Parameter |s| is the maximum stack depth (i.e., the largest excess of +|push| commands over |pop| commands) needed to process this file. Then +comes |t|, the total number of pages (|bop| commands) present. + +The postamble continues with font definitions, which are any number of +\\{fnt\_def} commands as described above, possibly interspersed with |nop| +commands. Each font number that is used in the \.{DVI} file must be defined +exactly twice: Once before it is first selected by a \\{fnt} command, and once +in the postamble. + +@ The last part of the postamble, following the |post_post| byte that +signifies the end of the font definitions, contains |q|, a pointer to the +|post| command that started the postamble. An identification byte, |i|, +comes next; this currently equals~2, as in the preamble. + +The |i| byte is followed by four or more bytes that are all equal to +the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of +these trailing bytes, until the total length of the file is a multiple of +four bytes, since this works out best on machines that pack four bytes per +word; but any number of 223's is allowed, as long as there are at least four +of them. In effect, 223 is a sort of signature that is added at the very end. +@^Fuchs, David Raymond@> + +This curious way to finish off a \.{DVI} file makes it feasible for +\.{DVI}-reading programs to find the postamble first, on most computers, +even though \TeX\ wants to write the postamble last. Most operating +systems permit random access to individual words or bytes of a file, so +the \.{DVI} reader can start at the end and skip backwards over the 223's +until finding the identification byte. Then it can back up four bytes, read +|q|, and move to byte |q| of the file. This byte should, of course, +contain the value 248 (|post|); now the postamble can be read, so the +\.{DVI} reader discovers all the information needed for typesetting the +pages. Note that it is also possible to skip through the \.{DVI} file at +reasonably high speed to locate a particular page, if that proves +desirable. This saves a lot of time, since \.{DVI} files used in production +jobs tend to be large. + +Unfortunately, however, standard \PASCAL\ does not include the ability to +@^system dependencies@> +access a random position in a file, or even to determine the length of a file. +Almost all systems nowadays provide the necessary capabilities, so \.{DVI} +format has been designed to work most efficiently with modern operating systems. +As noted above, \.{DVIIMP} will limit itself to the restrictions of standard +\PASCAL\ if |random_reading| is defined to be |false|. + +@* The imPRESS file format. +The format of an \.{imPRESS} file is quite similar in many ways to the +format of \.{DVI} files although, of course, the commands are all related +to the specific properties of the \.{IMAGEN} printer. For example, +dimensions are all in units that are derived from the inter-pixel distance +for the printer that is being used (1/300 of an inch on a 300 +pixels-per-inch printer). As far as we are concerned, an \.{imPRESS} file +consists of a sequence of bytes although, for some instructions the +associated parameters are made up of a collection of bits that are packed, +rather arbitrarily, +into one or more complete bytes (the commands themselves are never +split between bytes). + +As will be explained in more detail later, the \.{IMAGEN} printer provides +facilities for defining certain state variables and for saving and +restoring sets of these variable through the use of push and pop commands. + +The Imagen Corporation provides a publication-form-name that is used for +describing the commands and we will, so far as practical, use modified +forms of these publification-form-namess as our names for these commands, +simply prefacing the \.{IMAGEN} command name with \.{im} when this can be done +without making the name too long. +For consistancy, the same conventions are used to +specify the parameters as were used in module 15. + +For the reader's convenience, we will list these commands under the same +headings as used in the \.{imPRESS} Programmer's Manual. + +Document Structure Commands + +\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f| +such that the reference point of the character is at |(h,v)|. Then +increase |h| by the width of that character. Note that a character may +have zero or negative width, so one cannot be sure that |h| will advance +after this command; but |h| usually does increase. + +\yskip\hang|im_end_page| 219. This command declares the current page ready +for printing and starts page layout on a new page. State variables, which +are set once and remain in effect until changed, remain unchanged. These +include the current (|h|,|v|) position so these need to be reset as +desired. Note that some manipulation of data may be needed between a +\.{DVI} |eop| and an \.{imPRESS} |im_end_page|. + +\yskip\hang|im_eof| 255. Marks the end of the \.{imPRESS} document. Any +text after this command in the input file will be ignored. + +\yskip\hang|im_no_op| 254. May be used for padding and is ignored. May be +used as a direct translation for \.{DVI}'s |nop|. + +Coordinate System Commands + +\yskip\hang|set_hv_system| 205 [1]. This command selects the logical +coordinate that is to be used to lay out the pages. This command need not +be given if the default coordinates are to be used (with |h| and |v| axes +equivalent to those for |x| and |y|). The associated byte has a zero +first bit, the next two bits specify the origin, the next two bits specify +the axes and the final three bits specify the orientation. +For details, see the \.{imPRESS} User's Manual. + +\yskip\hang|set_abs_h| 135 [2]. Set the |h| to the value given in the +following 16-bit signed word. + +\yskip\hang|set_rel_h| 136 [2]. Add the value given in the following +16-bit signed word to |h|, + +\yskip\hang|set_abs_v| 137 [2]. Set the |v| to the value given in the +following 16-bit signed word. + +\yskip\hang|set_rel_v| 138 [2]. Add the value given in the following +16-bit signed word to |v|, + +Text Positioning Commands + +\yskip\hang|im_page| 213. Set both |h| and |v| to zero. + +\yskip\hang|im_set_adv_dirs| 206 [1]. Set the main and secondary advance +directions as specified in the following byte. The default direction +corresponde to normal english usage. +For details, see the \.{imPRESS} User's Manual. + +\yskip\hang|im_mmove| 133 [2]. Displace the current |h|,|v| position in the +main advance direction by the value in the following signed 16notbit +word. With the default value for |im_set_adv_dirs| this command is the +same as |im_set_rel_h|. + +\yskip\hang|im_smove| 134 [2]. Displace the current |h|,|v| position in the +secondary advance direction by the value in the following signed 16notbit +word. With the default value for |im_set_adv_dirs| this command is the +same as |im_set_rel_v|. + +\yskip\hang|im_set_sp| 210 [2]. Set the current inter-word spacing to +the value in the following 16-bit signed word. +We will not use this command as \TeX\ normally handles this matter. + +\yskip\hang|im_sp| 128. This command performs an inter-word space of the +size specified by the |im_set_sp| command. +We will not use this command as \TeX\ normally handles this matter. + +\yskip\hang|im_sp1| 129. This command performs an inter-word space of the +size one pixel greater than that specified by the |im_set_sp| command. +We will not use this command as \TeX\ normally handles this matter. + +\yskip\hang|im_mplus| 131. This command adjusts the current position by one +pixel in the main advance direction, that is normally to add one to the +current value of |h|. + +\yskip\hang|im_mminus| 132. This command adjusts the current position by +minus one pixel in the main advance direction, that is normally to +subtract one from the current value of |h|. + +\yskip\hang|im_crlf| 197. With no special advance directions, this command +sets |h| to the beginning-of-line value and advances |v| by the inter-line +space amount. + +\yskip\hang|im_set_bol| 209 [2]. Set the beginning-of-line margin to the +value specified in the following signed 16-bit word. + +\yskip\hang|im_set_il| 208 [2]. Set the inter-line space to the value +given in the following signed 16-bit word. + +Text Printing Commnds + +\yskip\hang|im_bgly| 199 [12 plus mask]. This command is used to download +glyphs defined by two bytes specifying <rotation, family, and member>, and +specified by two bytes each for the following four parameters, +width, left-offset, height, and top-offset, and finally by a mask +specifying the complete raster for the glyph within a minimum sized +bounding box (padded at the right with enough empty (white) pixels to +complete an otherwise partially filled byte). The rows are orderd starting +with the top row. The number of bits for this mask is then |((width+7) div +8)*height|. Once the rotation and family have been stated, a series of glyphs +from this family may be printed by a string of bytes containing their member +numbers. + +\yskip\hang|set_family| 207 [1]. This command sets the current-family to +|family| which must lie in the range from 0 to 95. + +\yskip\hang|im_member| 0-127. An \.{imPRESS} command code in the range +from 0 and 127 is a member command, calling for the designated member of +the current family to be printed at the current position and for the +printer to advance in the main advance direction by the glyph's associated +advance-width value. + +Resident Glyphs + +Normally, we will make no use of the resident glyphs provided by the +\.{IMAGEN} processor, since \TeX\ has no knowledge of these. These fonts +are not accessed directly but must be referenced indirectly through member +maps and family tables. For completeness, the commands that are used to +create these maps and family tables are here listed. For details see the +\.{imPRESS} User's Manual. + +\yskip\hang|create_map| 222 + +\yskip\hang|create_family_table| 221. + +Text Rule Command + +\yskip\hang|im_brule| 193 w[2] h[2] t[2]. This command prints a rectangle +(either in black or textured) of width w and height h with a top-offset +of t where a positive value means below the current position. + +State Saving and Restoring + +\yskip\hang|set_push_mask| 214 [2]. This command specifies which of the +various state variables are to be saved. Nine variables, set by the last 9 +bits (with the first 7 bits set to zero) of the associated 16-bit word are +involved, these being: pen-and-texture, interword-space, +beginning-of-line, family, hv-position, advance-direction, origin, and +orientation. These are all marked for saving (set to one) at the beginning +of each document and remain so unless changed by this command. + +\yskip\hang|im_push| 211. Save the state variables as prespecified +originally or as altered by the |set_push_mask| command. + +\yskip\hang|im_pop| 212. Restore the state variables saved by the most +recent unmatched |im_push| command. + + +@ @d im_sp=128 {advance one space} +@d im_sp1=129 {advance one space plus 1 pixel} +@d im_mplus=131 {advance one pixel} +@d im_mminus=132 {back up one pixel} +@d im_mmove=133 {move in the main advance direction} +@d im_smove=134 {move in the secondary advance direction} +@d set_abs_h=135 {move to |h| position} +@d set_rel_h=136 {move in the |h| direction} +@d set_abs_v=137 {move to |v| position} +@d set_rel_v=138 {move in the |v| direction} +@d circ_arc=150 {define a circular path} +@d ellipse_arc=151 {define an eliptical path} +@d circ_segm=160 {define a pie-shaped path} +@d im_brule=193 {print a rule} +@d im_crlf=197 {move to the beginning of th next line} +@d im_bgly=199 {define a downloaded glyph} +@d set_hv_system=205 {select a logical coordinate system} +@d im_set_adv_dirs=206 {set the advance directions} +@d set_family=207 {set current-family to family} +@d im_set_il=208 {set inter-line spacing} +@d im_set_bol=209 {set margin} +@d im_set_sp=210 {set inter-word spacing} +@d im_push=211 {save the state variables} +@d im_pop=212 {restore the state variables} +@d im_page=213 {set both |h| and |v| to zero} +@d set_push_mask=214 {specify variables to save} +@d im_end_page=219 {end the page} +@d create_family_table=221 {define a family table} +@d create_map=222 {create a member map} +@d set_pum=225 {append new path or replace path} +@d create_path=230 {define a path of segments} +@d set_texture=231 {select a texture for drawing} +@d set_pen=232 {select a pen width (in pixels)} +@d fill_path=233 {shade the ares inside the path} +@d draw_path=234 {draw the current path (a line)} +@d bitmap=235 {print a full bitmap} +@d set_magnification=236 {magnify the page (by 1, 2, or 4)} +@d define_macro=242 {define a macro} +@d execute_macro=243 {execute the named macro} +@d im_no_op=254 {no operation} +@d im_eof=255 {end the document} + + +@* Input and Output for binary files. +We have seen that a \.{DVI} file is a sequence of 8-bit bytes. The bytes +appear physically in what is called a `|packed file of 0..255|' +in \PASCAL\ lingo. + +Packing is system dependent, and many \PASCAL\ systems fail to implement +such files in a sensible way (at least, from the viewpoint of producing +good production software). For example, some systems treat all +byte-oriented files as text, looking for end-of-line marks and such +things. Therefore some system-dependent code is often needed to deal with +binary files, even though most of the program in this section of +\.{DVIIMP} is written in standard \PASCAL. +@^system dependencies@> + +One common way to solve the problem is to consider files of |integer| +numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to +a sequence of four bytes $(a,b,c,d)$ using the following code, which +avoids the controversial integer division of negative numbers: +$$\vbox{\halign{#\hfil\cr +|if x>=0 then a:=x div @'100000000|\cr +|else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr +\quad|end|\cr +|x:=x mod @'100000000;|\cr +|b:=x div @'200000; x:=x mod @'200000;|\cr +|c:=x div @'400; d:=x mod @'400;|\cr}}$$ +The four bytes are then kept in a buffer and output one by one. (On 36-bit +computers, an additional division by 16 is necessary at the beginning. +Another way to separate an integer into four bytes is to use/abuse +\PASCAL's variant records, storing an integer and retrieving bytes that are +packed in the same place; {\sl caveat implementor!\/}) It is also desirable +in some cases to read a hundred or so integers at a time, maintaining a +larger buffer. + +We shall stick to simple \PASCAL\ in this program, for reasons of clarity, +even if such simplicity is sometimes unrealistic. + +@<Types...@>= +@!eight_bits=0..255; {unsigned one-byte quantity} +@!byte_file=packed file of eight_bits; {files that contain binary data} + +@ The program deals with four binary file variables: |dvi_file| is the main +input file that we are translating into symbolic form, |gf_file| is +the generic font file from which the font information is being read, +|tfm_file| is the font-metric file that is used for width information +in those cases where this information is available but the corresponding +|gf_file| is not, and +|im_file| is the output file that is to be sent to the \.{IMAGEN} printer. + +@<Glob...@>= +@!dvi_file:byte_file; {the stuff we are transcribing to the IMAGEN} +@!gf_file:byte_file; {a generic font file} +@!tfm_file:byte_file; {a generic font file} +@!im_file:byte_file; {the output file} + +@ Special considerations are involved in restricting the range of pages +that one may want to print since the |count[0]| numbers that the |dvi| +file reports may be either positive (the usual case) or negative to signal +that the page numbers are to be printed in italics. We will assume that the +user will specify, 1) the |count[0]| number of the first page that he or +she will wants printed, by typing `/f' followed by this number, and 2) the +tolal number of pages to be printed (in the order that they occur in the +\.{DVI} file), by typing `/n' again followed by the number wanted. The +\.{Imagen} will, of course, actually deliver these pages in reverse order. +The method of reading these numbers is system dependent and the procedures +for calling |read_f|, etc., will be found in the change file. +Note that only |count[0]| will be used (as defined +in \.{PLAIN} and the remaining |count| numbers will be ignored. + +@<Glob...@>= +@!start_page:integer; {the requested starting page |count[0]| number} +@!num_pages:integer; {the requested number of pages to be printed} +@!f_count,l_count:integer; {backward counts used when printing partial file} +@!f_flag,n_flag:boolean; {true when /f and /n are specified} +@!page_match:boolean; {true when starting page is found} +@!counter:integer; {used in back-counting pages} +@!copies:integer; {the number of copies requested} + +@ @<Set init...@>= +counter:=0; f_count:=max_pages; l_count:=0; +num_pages:=max_pages; copies:=1; +f_flag:=false; n_flag:=false; +resolution:=300.0; +h_org:=round(resolution); v_org:=round(resolution); + +@ The following procedures will be needed. + +@p function read_int:integer; +var i:integer; +@!neg_flag:boolean; +begin +neg_flag:=false; i:=0; +get(tty); +while tty^=' ' do get(tty); +if (tty^='-') then neg_flag:=true; +while (tty^='-') or (tty^='+') do get(tty); +while (tty^>='0') and (tty^<='9') do begin + i:=i*10+xord[tty^]-"0"; get(tty); + end; +if neg_flag then i:=-i; +read_int:=i; +end; +@# +procedure read_f; +begin +start_page:=read_int; +f_flag:=true; +end; +@# +procedure read_n; +begin +num_pages:=read_int; +if num_pages=0 then num_pages:=max_pages; +n_flag:=true; +end; +@# +procedure read_c; +begin +copies:=read_int; +end; +@# +procedure read_h; +begin +h_org:=read_int; +end; +@# +procedure read_v; +begin +v_org:=read_int; +end; + +@ To prepare the input files, we |reset| them. An extension of +\PASCAL\ is needed in the case of |gf_file| and of |tfm_file|, +since we want to associate them +with external files whose names are specified dynamically (i.e., not +known at compile time). The following code assumes that `|reset(f,s)|' +does this, when |f| is a file variable and |s| is a string variable that +specifies the file name. If |eof(f)| is true immediately after +|reset(f,s)| has acted, we assume that no file named |s| is accessible. +@^system dependencies@> + +@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|} +begin reset(dvi_file); +cur_loc:=0; +end; +@# +procedure open_gf_file; {prepares to read packed bytes in |gf_file|} +begin reset(gf_file,cur_name); +cur_gf_loc:=0; +end; +@# +procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|} +begin reset(tfm_file,cur_tfm_name); +end; + +@ To prepare the |im_file| for output, we |rewrite| it. + +@p procedure open_im_file; {prepares to write packed bytes in |im_file|} +begin rewrite(im_file); im_byte_no:=0; +end; + +@ If you looked carefully at the preceding code, you probably asked, +``What are |cur_loc| and |cur_name|?'' Good question. They're global +variables: |cur_loc| is the number of the byte about to be read next from +|dvi_file|, and |cur_name| is a string variable that will be set to the +generic font file name before |open_gf_file| is called. While we are at +it, we will also declare |cur_gf_loc|. + +@<Glob...@>= +@!cur_loc:integer; {where we are about to look, in |dvi_file|} +@!cur_gf_loc:integer; {where we are about to look, in |gf_file|} +@!cur_name:packed array[1..name_length] of char; {external name, + with no lower case letters} +@!cur_tfm_name:packed array[1..name_length] of char; {external name, + with no lower case letters} +@!im_byte_no:integer; {where we are about to write, in |im_file|} + +@ We shall use a set of simple functions to read the next byte or bytes +from a |gf_file|. +@^system dependencies@> + +@p function gf_byte:integer; {returns the next byte, unsigned} +var b:eight_bits; +begin if eof(gf_file) then gf_byte:=0 +else begin read(gf_file,b); incr(cur_gf_loc); gf_byte:=b; + end; +end; +@# +function gf_two_bytes:integer; {returns the next two bytes, unsigned} +var a,@!b:eight_bits; +begin read(gf_file,a); read(gf_file,b); +cur_gf_loc:=cur_gf_loc+2; +gf_two_bytes:=a*256+b; +end; +@# +function gf_three_bytes:integer; {returns the next three bytes, unsigned} +var a,@!b,@!c:eight_bits; +begin read(gf_file,a); read(gf_file,b); read(gf_file,c); +cur_gf_loc:=cur_gf_loc+3; +gf_three_bytes:=(a*256+b)*256+c; +end; +@# +function gf_signed_quad:integer; {returns the next four bytes, signed} +var a,@!b,@!c,@!d:eight_bits; +begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d); +cur_gf_loc:=cur_gf_loc+4; +if a<128 then gf_signed_quad:=((a*256+b)*256+c)*256+d +else gf_signed_quad:=(((a-256)*256+b)*256+c)*256+d; +end; + +@ We will refer to \.{TFM} files for character width information in those +cases where \.{.GF} files are not available. We read four bytes at a +time, putting the input into global +variables |b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and +|b3| the fourth. + +@<Glob...@>= +@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once} + +@ The |read_tfm_word| procedure sets |b0| through |b3| to the next +four bytes in the current \.{TFM} file. +@^system dependencies@> + +@p procedure read_tfm_word; +begin read(tfm_file,b0); read(tfm_file,b1); +read(tfm_file,b2); read(tfm_file,b3); +end; + +@ We shall use another set of simple functions to read the next byte or +bytes from |dvi_file|. There are seven possibilities, each of which is +treated as a separate function in order to minimize the overhead for +subroutine calls. +@^system dependencies@> + +@p function get_byte:integer; {returns the next byte, unsigned} +var b:eight_bits; +begin if eof(dvi_file) then get_byte:=0 +else begin read(dvi_file,b); incr(cur_loc); get_byte:=b; + end; +end; +@# +function signed_byte:integer; {returns the next byte, signed} +var b:eight_bits; +begin read(dvi_file,b); incr(cur_loc); +if b<128 then signed_byte:=b @+ else signed_byte:=b-256; +end; +@# +function get_two_bytes:integer; {returns the next two bytes, unsigned} +var a,@!b:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); +cur_loc:=cur_loc+2; +get_two_bytes:=a*256+b; +end; +@# +function signed_pair:integer; {returns the next two bytes, signed} +var a,@!b:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); +cur_loc:=cur_loc+2; +if a<128 then signed_pair:=a*256+b +else signed_pair:=(a-256)*256+b; +end; +@# +function get_three_bytes:integer; {returns the next three bytes, unsigned} +var a,@!b,@!c:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); +cur_loc:=cur_loc+3; +get_three_bytes:=(a*256+b)*256+c; +end; +@# +function signed_trio:integer; {returns the next three bytes, signed} +var a,@!b,@!c:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); +cur_loc:=cur_loc+3; +if a<128 then signed_trio:=(a*256+b)*256+c +else signed_trio:=((a-256)*256+b)*256+c; +end; +@# +function signed_quad:integer; {returns the next four bytes, signed} +var a,@!b,@!c,@!d:eight_bits; +begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d); +cur_loc:=cur_loc+4; +if a<128 then signed_quad:=((a*256+b)*256+c)*256+d +else signed_quad:=(((a-256)*256+b)*256+c)*256+d; +end; + +@ Finally we come to the routines that are used only if |random_reading| is +|true|. The driver program below needs two such routines: |dvi_length| should +compute the total number of bytes in |dvi_file|, possibly also +causing |eof(dvi_file)| to be true; and |move_to_byte(n)| +should position |dvi_file| so that the next |get_byte| will read byte |n|, +starting with |n=0| for the first byte in the file. +@^system dependencies@> + +Such routines are, of course, highly system dependent. They are implemented +here in terms of two assumed system routines called |set_pos| and |cur_pos|. +The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is +negative or larger than the total number of items in |f|; in the latter +case, |set_pos(f,n)| moves to the end of file |f|. +The call |cur_pos(f)| gives the total number of items in |f|, if +|eof(f)| is true; we use |cur_pos| only in such a situation. + +@p function dvi_length:integer; +begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file); +end; +@# +procedure move_to_byte(n:integer); +begin set_pos(dvi_file,n); cur_loc:=n; +end; + +@ We face a similar problem in dealing with the \.{GF} files so perhaps we +should deal with this problem at this time. We will need two special +routines, one to determine the byte length of the individual \.{GF} files +and the second to position |gf_file| so that the next |gf_byte| will read +byte |n|, starting with |n=0| for the first byte in the file. +@^system dependencies@> + +@p function gf_length:integer; +begin set_pos(gf_file,-1); gf_length:=cur_pos(gf_file); +end; +@# +procedure move_to_gf_byte(n:integer); +begin set_pos(gf_file,n); cur_gf_loc:=n; +end; + +@ We will also need a simple way of sending bytes, unsigned bytes, and signed +16-bit words to the |im_file|. While the \.{imPRESS} manual user |u_byte| +for an unsigned byte, we will attach an `s' prefix for the signed case, leaving +|im_byte| to mean an unsigned byte as used elsewhere in this program. + +@d im_byte(#)==begin write(im_file,#); + incr(im_byte_no); end + +@p procedure im_sbyte(@!w:integer); +begin +if w<0 then w:=w+@"100; +im_byte(w); +end; +@# +procedure im_halfword(@!w:integer); +begin +if w<0 then w:=w+@"10000; +im_byte(w div @"100); +im_byte(w mod @"100); +end; + +@* GF file format. +This program, in contrast with many device drivers, gets its font +information directly from the ``generic font'' (\.{GF}) files that are the +most important output produced by the \MF\ program. The term {\sl +generic\/} indicates that this file format doesn't match the conventions +of any name-brand manufacturer; but it is easy to convert \.{GF} files to +the special format required by almost all digital phototypesetting +equipment, if these devices are designed to accept fonts directly. +Alternately, one can translate the \.{GF} and pass the needed raster +information on to the printer at the time that a \.{DVI} file is being +processed, as is done in this program. + +There's a strong analogy +between the \.{DVI} files written by \TeX\ and the \.{GF} files written +by \MF; and, in fact, the file formats have a lot in common. + +A \.{GF} file is a stream of 8-bit bytes that may be +regarded as a series of commands in a machine-like language. The first +byte of each command is the operation code, and this code is followed by +zero or more bytes that provide parameters to the command. The parameters +themselves may consist of several consecutive bytes; for example, the +`|boc|' (beginning of character) command has six parameters, each of +which is four bytes long, while the shortened, more ofter used, form, `|boc1|' +has five parameters, each of which is only one byte long. +Parameters are usually regarded as nonnegative +integers; but four-byte-long parameters can be either positive or +negative, hence they range in value from $-2^{31}$ to $2^{31}-1$. +As in \.{TFM} files, numbers that occupy +more than one byte position appear in BigEndian order, +and negative numbers appear in two's complement notation. + +A \.{GF} file consists of a ``preamble,'' followed by a sequence of one or +more ``characters,'' followed by a ``postamble.'' The preamble is simply a +|pre| command, with its parameters that introduce the file; this must come +first. Each ``character'' consists of a |boc| or a |boc1| +command, followed by any +number of other commands that specify ``black'' pixels, +followed by an |eoc| command. The characters appear in the order that \MF\ +generated them. If we ignore no-op commands (which are allowed between any +two commands in the file), each |eoc| command is immediately followed by a +|boc| or a |boc1| +command, or by a |post| command; in the latter case, there are no +more characters in the file, and the remaining bytes form the postamble. +Further details about the postamble will be explained later. + +Some parameters in \.{GF} commands are ``pointers.'' These are four-byte +quantities that give the location number of some other byte in the file; +the first file byte is number~0, then comes number~1, and so on. + +@ The \.{GF} format is intended to be both compact and easily interpreted +by a machine. Compactness is achieved by making most of the information +relative instead of absolute. When a \.{GF}-reading program reads the +commands for a character, it keeps track of two quantities: (a)~the current +column number,~|m|; and (b)~the current row number,~|n|. These are 32-bit +signed integers, although most actual font formats produced from \.{GF} +files will need to curtail this vast range because of practical +limitations. (\MF\ output will never allow $\vert m\vert$ or $\vert +n\vert$ to exceed 4096, but the \.{GF} format tries to be more general.) + +How do \.{GF}'s row and column numbers correspond to the conventions +of \TeX\ and \MF? Well, the ``reference point'' of a character, in \TeX's +view, is considered to be at the lower left corner of the pixel in row~0 +and column~0. This point is the intersection of the baseline with the left +edge of the type; it corresponds to location $(0,0)$ in \MF\ programs. +Thus the pixel in \.{GF} row~0 and column~0 is \MF's unit square, comprising the +region of the plane whose coordinates both lie between 0 and~1. The +pixel in \.{GF} row~|n| and column~|m| consists of the points whose \MF\ +coordinates |(x,y)| satisfy |m<=x<=m+1| and |n<=y<=n+1|. Negative values of +|m| and~|x| correspond to columns of pixels {\sl left\/} of the reference +point; negative values of |n| and~|y| correspond to rows of pixels {\sl +below\/} the baseline. + +Besides |m| and |n|, there's also a third aspect of the current +state, namely the @!|paint_switch|, which is always either \\{black} or +\\{white}. Each \\{paint} command advances |m| by a specified amount~|d|, +and blackens the intervening pixels if |paint_switch=black|; then +the |paint_switch| changes to the opposite state. \.{GF}'s commands are +designed so that |m| will never decrease within a row, and |n| will never +increase within a character; hence there is no way to whiten a pixel that +has been blackened. \.{DVIIMP} does not use a |paint_switch| parameter, +as such, but other programs do and the concept is useful in following +the way that the |paint| commands are handled. + +@ Here is a list of all the commands that may appear in a \.{GF} file. Each +command is specified by its symbolic name (e.g., |boc|), its opcode byte +(e.g., 67), and its parameters (if any). The parameters are followed +by a bracketed number telling how many bytes they occupy; for example, +`|d[2]|' means that parameter |d| is two bytes long. + +\yskip\hang|paint_0| 0. This is a \\{paint} command with |d=0|; it does +nothing but change the |paint_switch| from \\{black} to \\{white} or vice~versa. + +\yskip\hang\\{paint\_1} through \\{paint\_63} (opcodes 1 to 63). +These are \\{paint} commands with |d=1| to~63, defined as follows: If +|paint_switch=black|, blacken |d|~pixels of the current row~|n|, +in columns |m| through |m+d-1| inclusive. Then, in any case, +complement the |paint_switch| and advance |m| by~|d|. + +\yskip\hang|paint1| 64 |d[1]|. This is a \\{paint} command with a specified +value of~|d|; \MF\ uses it to paint when |64<=d<256|. + +\yskip\hang|@!paint2| 65 |d[2]|. Same as |paint1|, but |d|~can be as high +as~65535. + +\yskip\hang|@!paint3| 66 |d[3]|. Same as |paint1|, but |d|~can be as high +as $2^{24}-1$. \MF\ never needs this command, and it is hard to imagine +anybody making practical use of it; surely a more compact encoding will be +desirable when characters can be this large. But the command is there, +anyway, just in case. + +\yskip\hang|boc| 67 |c[4]| |p[4]| |min_m[4]| |max_m[4]| |min_n[4]| +|max_n[4]|. Beginning of a character: Here |c| is the character code, and +|p| points to the previous character beginning (if any) for characters having +this code number modulo 256. (The pointer |p| is |-1| if there was no +prior character with an equivalent code.) The values of registers |m| and |n| +defined by the instructions that follow for this character must +satisfy |min_m<=m<=max_m| and |min_n<=n<=max_n|. (The values of |max_m| and +|min_n| need not be the tightest bounds possible.) When a \.{GF}-reading +program sees a |boc|, it can use |min_m|, |max_m|, |min_n|, and |max_n| to +initialize the bounds of an array. Then it sets |m:=min_m|, |n:=max_n|, and +|paint_switch:=white|. + +\yskip\hang|boc1| 68 |c[1]| |@!del_m[1]| |max_m[1]| |@!del_n[1]| |max_n[1]|. +Same as |boc|, but |p| is assumed to be~$-1$; also |del_m=max_m-min_m| +and |del_n=max_n-min_n| are given instead of |min_m| and |min_n|. +The one-byte parameters must be between 0 and 255, inclusive. +\ (This abbreviated |boc| saves 19~bytes per character, in common cases.) + +\yskip\hang|eoc| 69. End of character: All pixels blackened so far +constitute the pattern for this character. In particular, a completely +blank character might have |eoc| immediately following |boc|. + +\yskip\hang|skip0| 70. Decrease |n| by 1 and set |m:=min_m|, +|paint_switch:=white|. \ (This finishes one row and begins another, +ready to whiten the leftmost pixel in the new row.) + +\yskip\hang|skip1| 71 |d[1]|. Decrease |n| by |d+1|, set |m:=min_m|, and set +|paint_switch:=white|. This is a way to produce |d| all-white rows. + +\yskip\hang|@!skip2| 72 |d[2]|. Same as |skip1|, but |d| can be as large +as 65535. + +\yskip\hang|@!skip3| 73 |d[3]|. Same as |skip1|, but |d| can be as large +as $2^{24}-1$. \MF\ obviously never needs this command. + +\yskip\hang|new_row_0| 74. Decrease |n| by 1 and set |m:=min_m|, +|paint_switch:=black|. \ (This finishes one row and begins another, +ready to {\sl blacken\/} the leftmost pixel in the new row.) + +\yskip\hang|@!new_row_1| through |@!new_row_164| (opcodes 75 to 238). Same as +|new_row_0|, but with |m:=min_m+1| through |min_m+164|, respectively. + +\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in +general; it functions as a $(k+2)$-byte |no_op| unless special \.{GF}-reading +programs are being used. \MF\ generates \\{xxx} commands when encountering +a \&{special} string; this occurs in the \.{GF} file only between +characters, after the preamble, and before the postamble. However, +\\{xxx} commands might appear anywhere in \.{GF} files generated by other +processors. It is recommended that |x| be a string having the form of a +keyword followed by possible parameters relevant to that keyword. + +\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|. + +\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|. +\MF\ uses this when sending a \&{special} string whose length exceeds~255. + +\yskip\hang|@!xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be +ridiculously large; |k| mustn't be negative. + +\yskip\hang|yyy| 243 |y[4]|. This command is undefined in general; +it functions as a 5-byte |no_op| unless special \.{GF}-reading programs +are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a +result of \&{numspecial} commands; the intent is to provide numeric +parameters to \\{xxx} commands that immediately precede. + +\yskip\hang|no_op| 244. No operation, do nothing. Any number of |no_op|'s +may occur between \.{GF} commands, but a |no_op| cannot be inserted between +a command and its parameters or between two parameters. + +\yskip\hang|char_loc| 245 |c[1]| |dx[4]| |dy[4]| |w[4]| |p[4]|. +This command will appear only in the postamble, which will be explained shortly. + +\yskip\hang|@!char_loc0| 246 |c[1]| |@!dm[1]| |w[4]| |p[4]|. +Same as |char_loc|, except that |dy| is assumed to be zero, and the value +of~|dx| is taken to be |65536*dm|, where |0<=dm<256|. + +\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]|. +Beginning of the preamble; this must come at the very beginning of the +file. Parameter |i| is an identifying number for \.{GF} format, currently +131. The other information is merely commentary; it is not given +special interpretation like \\{xxx} commands are. (Note that \\{xxx} +commands may immediately follow the preamble, before the first |boc|.) + +\yskip\hang|post| 248. Beginning of the postamble, see below. + +\yskip\hang|post_post| 249. Ending of the postamble, see below. + +\yskip\noindent Commands 250--255 are undefined at the present time. + +@d gf_id_byte=131 {identifies the kind of \.{GF} files described here} + +@ Here are the opcodes that \.{DVIIMP} actually refers to. + +@d paint_0=0 {beginning of the \\{paint} commands} +@d paint1=64 {move right a given number of columns, then + black${}\swap{}$white} +@d paint2=65 +@d boc=67 {beginning of a character} +@d boc1=68 {abbreviated |boc|} +@d eoc=69 {end of a character} +@d skip0=70 {skip no blank rows} +@d skip1=71 {skip over blank rows} +@d skip2=72 {skip over blank rows} +@d new_row_0=74 {move down one row and then right} +@d new_row_164=238 {move down 164 rows and then right} +{xxx1=239 defined previously} +@d yyy=243 {for \&{numspecial} numbers} +@d no_op=244 {no operation} +@d char_loc=245 {character locators in the postamble} +{pre=247 (preamble) defined previously} +{post 248 (postamble beginning) defined previously} +{|post_post|=249 (postamble ending) defined previously} +{undefined commands==250,251,252,253,254,255} + +@ The last character in a \.{GF} file is followed by `|post|'; this command +introduces the postamble, which summarizes important facts that \MF\ has +accumulated. The postamble has the form +$$\vbox{\halign{\hbox{#\hfil}\cr + |post| |p[4]| |@!ds[4]| |@!cs[4]| |@!hppp[4]| |@!vppp[4]| + |min_m[4]| |max_m[4]| |min_n[4]| |max_n[4]|\cr + $\langle\,$character locators$\,\rangle$\cr + |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$ +Here |p| is a pointer to the byte following the final |eoc| in the file +(or to the byte following the preamble, if there are no characters); +it can be used to locate the beginning of \\{xxx} commands +that might have preceded the postamble. The |ds| and |cs| parameters +@^design size@> @^check sum@> +give the design size and check sum, respectively, which are exactly the +values put into the header of any \.{TFM} file that shares information with this +\.{GF} file. Parameters |hppp| and |vppp| are the ratios of +pixels per point, horizontally and vertically, expressed as |scaled| integers +(i.e., multiplied by $2^{16}$); they can be used to correlate the font +with specific device resolutions, magnifications, and ``at sizes.'' Then +come |min_m|, |max_m|, |min_n|, and |max_n|, which bound the values that +registers |m| and~|n| assume in all characters in this \.{GF} file. +(These bounds need not be the best possible; |max_m| and |min_n| may, on the +other hand, be tighter than the similar bounds in |boc| commands. For +example, some character may have |min_n=-100| in its |boc|, but it might +turn out that |n| never gets lower than |-50| in any character; then +|min_n| can have any value |<=-50|. If there are no characters in the file, +it's possible to have |min_m>max_m| and/or |min_n>max_n|.) + +@ Character locators are introduced by |char_loc| commands, +which specify a character residue~|c|, character displacements (|dx,dy|), +a character width~|w|, and a pointer~|p| +to the beginning of that character. (If two or more characters have the +same code~|c| modulo 256, only the last will be indicated; the others can be +located by following backpointers. Characters whose codes differ by a +multiple of 256 are assumed to share the same font metric information, +hence the \.{TFM} file contains only residues of character codes modulo~256. +This convention is intended for oriental languages, when there are many +character shapes but few distinct widths.) +@^oriental characters@>@^Chinese characters@>@^Japanese characters@> + +The character displacements (|dx,dy|) are the values of \MF's \&{chardx} +and \&{chardy} parameters; they are in units of |scaled| pixels; +i.e., |dx| is in horizontal pixel units times $2^{16}$, and |dy| is in +vertical pixel units times $2^{16}$. This is the intended amount of +displacement after typesetting the character; for \.{DVI} files, |dy| +should be zero, but other document file formats allow nonzero vertical +displacement. + +The character width~|w| duplicates the information in the \.{TFM} file; it +is $2^{24}$ times the ratio of the true width to the font's design size. + +The backpointer |p| points to the character's |boc|, or to the first of +a sequence of consecutive \\{xxx} or |yyy| or |no_op| commands that +immediately precede the |boc|, if such commands exist; such ``special'' +commands essentially belong to the characters, while the special commands +after the final character belong to the postamble (i.e., to the font +as a whole). This convention about |p| applies also to the backpointers +in |boc| commands, even though it wasn't explained in the description +of~|boc|. @^backpointers@> +@^oriental characters@>@^Chinese characters@>@^Japanese characters@> + +Pointer |p| might be |-1| if the character exists in the \.{TFM} file +but not in the \.{GF} file. This unusual situation can arise in \MF\ output +if the user had |proofing<0| when the character was being shipped out, +but then made |proofing>=0| in order to get a \.{GF} file. + +These |p| pointers are not currently being used in this program, instead we +store all rasters as received in the |mm_store| and index then by +|glyph_ptr|. The role of a |-1| value for |p| is take over by a |-1| in +the |glyph_ptr| array. + +@ The last part of the postamble, following the |post_post| byte that +signifies the end of the character locators, contains |q|, a pointer to the +|post| command that started the postamble. An identification byte, |i|, +comes next; this currently equals~131, as in the preamble. + +The |i| byte is followed by four or more bytes that are all equal to +the decimal number 223 (i.e., @'337 in octal). \MF\ puts out four to seven of +these trailing bytes, until the total length of the file is a multiple of +four bytes, since this works out best on machines that pack four bytes per +word; but any number of 223's is allowed, as long as there are at least four +of them. In effect, 223 is a sort of signature that is added at the very end. +@^Fuchs, David Raymond@> + +This curious way to finish off a \.{GF} file makes it feasible for +\.{GF}-reading programs to find the postamble first, on most computers, +even though \MF\ wants to write the postamble last. Most operating +systems permit random access to individual words or bytes of a file, so +the \.{GF} reader can start at the end and skip backwards over the 223's +until finding the identification byte. Then it can back up four bytes, read +|q|, and move to byte |q| of the file. This byte should, of course, +contain the value 248 (|post|); now the postamble can be read, so the +\.{GF} reader can discover all the information needed for individual characters. + +Unfortunately, however, standard \PASCAL\ does not include the ability to +@^system dependencies@> +access a random position in a file, or even to determine the length of a file. +Almost all systems nowadays provide the necessary capabilities, so \.{GF} +format has been designed to work most efficiently with modern operating systems. +But if \.{GF} files have to be processed under the restrictions of standard +\PASCAL, one can simply read them from front to back. This will +be adequate for most applications. However, the postamble-first approach +would facilitate a program that merges two \.{GF} files, replacing data +from one that is overridden by corresponding data in the other. + +@* Reading the font information. +\.{DVI} file format does not include information about character widths +nor the detailed raster information. \.{DVIIMP} gets this information +directly from the (\.{GF}) files. +@.GF {\rm files}@> + +The task facing \.{DVIIMP} is quite different from that facing \.{DVItype} +which has a comparatively easy task in this regard, since it needs only a +few words of information from each font. We will follow this earlier +program as much as possible in our use of file names and related details +but our data structure will necessarily be somewhat more complicated. + +We follow \.{DVItype} to the extent of listing the current number of known +fonts as |nf|. Each known font has an internal number |f|, where |0<=f<nf|; +the external number of this font, i.e., its font identification number in +the \.{DVI} file, is |font_num[f]|, and the external name of this font is +the string that occupies positions |font_name[f]| through +|font_name[f+1]-1| of the array |names|. The latter array consists of +|ASCII_code| characters, and |font_name[nf]| is its first unoccupied +position. + +Fonts containing more than 128 characters require special attention since +\.{Imagen} will only accept 0 to 127 as valid character numbers. An easy +way out of this difficulty is to assign |f| numbers starting at the +largest font number (95) that \.{Imagen} will accept (and progressing +downward) as |im_extension| family numbers that can be assigned to the +over 127 characters of large fonts and that can be downloaded with |c-128| +as the \.{Imagen} identification. A record of this relationship is +maintained in an |im_extension[cur_font]| array. + +We will find it necessary, occasionally, to reuse the |mm_store| space +and to make this possible we define a |free_limit| parameter. +This parameter is set initially to |mm_max|. The following +|make_space| procedure is used to free space. +Note that this does not prevent the printing of those glyphs that have +been downloaded but the raster data for those glyphs that have not been +downloaded will have to be reread from the |gf| file should any of these +be subsequently requested. + +@p procedure make_space; +var i,j,k,q: integer; +begin +@!debug +print(' overwriting font '); +print_ln(font_order[0]:1,' '); +gubed@/ +j:=data_start[font_order[0]]; +k:=data_start[font_order[1]]; +q:=glyph_ptr[k]; +if q>12 then free_limit:=q-1 else free_limit:=mm_size; +for i:=j to k-1 do + if glyph_ptr[i]>=4 then glyph_ptr[i]:=0; {mark as no longer available} +for i:=0 to max_fonts-1 do font_order[i]:=font_order[i+1]; +end; + +@ We also follow the \.{DVItype} example of storing the glyph widths +(measured in \.{DVI} units) in a |width| array that is indexed by values +stored in a |data_base| array. This |data_base| is in turn indexed by the +internal font number and its values point to pseudo starting locations in +the |width| array where the first glyph widths for the fonts would be +stored were there a zero numbered glyph in the font. The actualy starting +location for each font's data in the |width| table is displaced forward +by |font_bc| where |font_bc| is the lowest character number that is +contained in each particular font. The values in the |data_base| array +are, of course, also used to access the |pixel_width| values (measured in +pixels) since it will be organized in an identical way to that used with +the |width| table. + +Gaining access to the font raster details, stored in |mm_store|, is a +slightly longer process because the spaces occupied by the raster details +will usually vary from glyph to glyph. We handle this matter by having +yet another indexing stage where the starting location in |mm_store| for +each individual glyph is stored in a |glyph_ptr| array that is accessed, +in turn, by using the same |data_base| value that is used to locate the +|width| and |pixel_width| values. + +Normally, this double-indexing recall needs be done but once for +each used glyph since all glyphs are stored internally in the \.{IMAGEN} +on the first occasions when they are used. As will be noted later, we +signal the fact that any particular glyph has been down-loaded by +negating its reference number in the |glyph_ptr| array. + +@d char_width_end(#)==#] +@d char_width(#)==width[data_base[#]+char_width_end +@d invalid_width==@'17777777777 +@d stow(#)==begin mm_store[m1,m2]:=#; + if (mm<free_limit) and ((mm+8)>free_limit) then make_space; + if m2<m2_max then begin incr(m2); incr(mm); end + else + begin + m2:=4; {|-4<m2<4| freed for down-loading and |make_space| signs} + mm:=mm+5; + if m1<m1_max then incr(m1) else begin m1:=0; mm:=4; end; + end; + end + +@<Glob...@>= +@!font_num:array [0..max_fonts] of integer; {external font numbers} +@!font_m_val:array [0..max_fonts] of integer; {overall font magnification} +@!font_name:array [0..max_fonts] of 0..name_size; {starting positions + of external font names} +@!names:array [0..name_size] of ASCII_code; {characters of names} +@!font_check_sum:array [0..max_fonts] of integer; {check sums} +@!font_scaled_size:array [0..max_fonts] of integer; {scale factors} +@!font_design_size:array [0..max_fonts] of integer; {design sizes} +@!font_space:array [0..max_fonts] of integer; {boundary between ``small'' + and ``large'' spaces} +@!font_bc:array [0..max_fonts] of integer; {beginning characters in fonts} +@!font_ec:array [0..max_fonts] of integer; {ending characters in fonts} +@!data_base:array [0..max_fonts] of integer; {index into font data tables} +@!width:array [0..max_glyphs] of integer; {character widths, in \.{DVI} units} +@!in_width:array[0..255] of integer; {\.{TFM} width data in \.{DVI} units} +@!tfm_check_sum:integer; {check sum found in |tfm_file|} +@!nf:0..max_fonts; {the number of known fonts} +@!nf2: 0..95; {the lower limit of font extension numbers} +@!im_extension: array[0..max_fonts] of integer; {relating extension numbers} +@!width_ptr:0..max_glyphs; {the number of known character widths} +@!bc,ec:integer; {beginning and ending c in current font} +@!w_byte: array[0..max_char_no, 0..3] of eight_bits; {to hold |width| bytes} +@!gf_ptr: array[0..max_char_no] of integer; {to hold valid glyph indicators} + +@ @<Set init...@>= +nf:=0; width_ptr:=0; font_name[0]:=0; font_space[0]:=0; +nf2:=95; {limit to usable font numbers set by Imagen} +for i:=0 to max_fonts do im_extension[i]:=-1; {marked as not assigned} + +@ It is, of course, a simple matter to print the name of a given font. + +@p procedure print_font(@!f:integer); {|f| is an internal font number} +var k:0..name_size; {index into |names|} +begin if f=nf then print('UNDEFINED!') +@.UNDEFINED@> +else begin for k:=font_name[f] to font_name[f+1]-1 do + print(xchr[names[k]]); + end; +end; + +@ The following procedure is used to print the font-name extension as +used on the \.{SAIL} computer at Stanford. It condenses a possibly 4-digit +number into three characters by using the letters A to Z for the first character +for extensions in the range from 1000 to 3599 and simply reporting an extension +of .GF for those unlikely cases where the value is 3600 or greater. +@^system dependencies@> + +@p procedure print_extension(m:integer); +begin +print('.'); +if m < 3600 then + begin + if m < 1000 then print(xchr[(m div 100)+@'60]) + else print(xchr[(m div 100)+@'67]); + print(xchr[(m mod 100) div 10+@'60]); + print(xchr[m mod 10+@'60]); + end +else print('GF'); +end; + +@ The global variable |gf_check_sum| is set to the check sum that +appears in the current \.{GF} file. + +@<Glob...@>= +@!gf_check_sum:integer; {check sum found in |gf_file|} + +@ We will need a number of procedures to extract the necessary inforation +fron a \.{GF} file, assuming that the file has just been successfully +reset so that we are ready to read its first byte. Only a limited amount +of validity checking of the \.{GF} file will be done since \.{GF} files +are almost always valid, and since the \.{GFtype} utility program has been +specifically designed to diagnose \.{GF} errors. The procedure simply +returns |false| if it detects anything amiss in the \.{GF} data. + +Since we are going to defer the creation of an \.{imPRESS} |bgly| command +for each glyph until the first time that it is actually called, we will +now only decipher the |gf| commands far enough to determine if they are to +be saved and to store them away in as compact a form as possible. + +As mentioned earlier, raster determining commands are stored in a large +array, |mm_store|. This information is stored serially, as it is received, +together with 8 bytes of preliminary information that must also be +transmitted. The location of the first byte of information is recorded +in the |glyph_ptr| array. To insure that this number will always be greater +than 3 (since numbers in the range between -3 and +3 are used as special +signals) we do not use the first 4 cells in |mm_store| (actually, the first +4 cells in each of the four sections into which |mm_store| is divided). +Later, when the glyph is first called for +by the \.{DVI} file, we will generate an appropriate \.{IMAGEN} |bgly| +command and complement the pointer value in the |glyph_ptr| array to show +that this has been done. Finally, as will be explained in more detail +later, we will have to arrange for the removal of the raster information +for one or more fonts, to make space for other fonts. and we will have to +store a record of this removal. + +We will find it convenient to define a |find_gf_postamble| function and a +|read_gf_postamble| procedure. Since we will have occasion to deal with +parameters associated with the GF commands, we will also define a function +|first_gf_par| analogous to the |first_par| that we defined earlier. + +@p function find_gf_postamble:boolean; +var q,@!k: integer; +begin +find_gf_postamble:=true; +gf_post_loc:=gf_length-4; +repeat if gf_post_loc=0 then find_gf_postamble:=false; +move_to_gf_byte(gf_post_loc); k:=gf_byte; decr(gf_post_loc); +until k<>223; +if k<>gf_id_byte then find_gf_postamble:=false; +move_to_gf_byte(gf_post_loc-3); q:=gf_signed_quad; +if (q<0)or(q>gf_post_loc-3) then find_gf_postamble:=false; +move_to_gf_byte(q); k:=gf_byte; +if k<>post then find_gf_postamble:=false; +@!debug +print_ln( ' gf postamble at ',cur_gf_loc:1); +gubed +end; + +@ Having found the |gf_postamble|, we must now read it and stow the +data away as as halfwords as required later by \.{IMAGEN}. + +@p procedure read_gf_postamble; +var k,l:integer; {loop indices} +@!p,q,@!m,@!c:integer; {general purpose registers} +begin gf_post_loc:=cur_gf_loc-1; +@.gf_postamble starts at byte n@> +p:=gf_signed_quad; +design_size:=gf_signed_quad; check_sum:=gf_signed_quad;@/ +hppp:=gf_signed_quad; vppp:=gf_signed_quad;@/ +magnification:=hppp/(65536.0*resolution/72.27); +@<Report font specification disagreements@>; +min_m:=gf_signed_quad; max_m:=gf_signed_quad; +min_n:=gf_signed_quad; max_n:=gf_signed_quad;@/ +bc:=max_char_no; ec:=0; + {prepare for a determination in Process the character loc} +@<Clear |w_byte| array@>; +@<Process the character locations in the postamble@>; +while not eof(gf_file) do m:=gf_byte; {to close out file} +end; +@# +function first_gf_par(o:eight_bits):integer; +begin case o of +sixty_four_cases(paint_0): first_gf_par:=o-paint_0; +paint1,skip1,char_loc,char_loc+1,xxx1: first_gf_par:=gf_byte; +paint2,skip2,xxx1+1: first_gf_par:=gf_two_bytes; +paint1+2,skip1+2,xxx1+2: first_gf_par:=gf_three_bytes; +xxx1+3,yyy: first_gf_par:=gf_signed_quad; +boc,boc1,eoc,skip0,no_op,pre,post,post_post,undefined_commands: first_gf_par:=0; +one_sixty_five_cases(new_row_0): first_gf_par:=o-new_row_0; +end; +end; +@# +procedure copy_byte; +var w:eight_bits; +begin +w:=gf_byte; stow(w); +end; +@# +procedure stow_pair(@!w:integer); +begin +stow(w div @"100); +stow(w mod @"100); +end; +@# +procedure stow_signed_pair(@!w:integer); +begin +if w<0 then w:=w+@"10000; +stow(w div @"100); +stow(w mod @"100); +end; + +@ @<Report font specification disagreements@>= +if design_size<>font_design_size[cur_font]*16 then + begin print_nl; print('design sizes for font '); print_font(cur_font); + print_extension(font_m_val[cur_font]); print(' do not agree. '); + end; +if (check_sum<>font_check_sum[cur_font]) and (check_sum<>0) + and (font_check_sum[cur_font]<>0) then + begin print_nl; print('check sums for font '); print_font(cur_font); + print_extension(font_m_val[cur_font]); print(' do not agree. '); + end; +q:=round((resolution*65536/72.27)*(mag/1000.0)* + font_scaled_size[cur_font]/font_design_size[cur_font]); +if ((q-(q div 100))>hppp) or ((q+(q div 100))<hppp) then + begin print_nl; print('at size values for font '); print_font(cur_font); + print_extension(font_m_val[cur_font]); + print(' disagree by more than one percent. '); + end; + +@ @<Clear |w_byte| array@>= +for k:=0 to max_char_no do + begin + for l:=0 to 3 do w_byte[k,l]:=0; + gf_ptr[k]:=0; {so data of missing glyphs will be made available} + end; + +@ @<Process the character locations in the postamble@>= +repeat k:=gf_byte; +if (k=char_loc) or (k=char_loc+1) then + begin + c:=gf_byte; + if c>max_char_no then abort('Character number too large'); + if c<bc then bc:=c; if c>ec then ec:=c; + if k=char_loc then + begin dx[c]:=gf_signed_quad div 65536; dy:=gf_signed_quad; + end + else begin dx[c]:=gf_byte; dy:=0; + end; +@!debug + print(' k=',k:1,' c=',c:1,' dx=',dx[c]:1); +gubed@/ + w_byte[c,0]:=gf_byte; + w_byte[c,1]:=gf_byte; + w_byte[c,2]:=gf_byte; + w_byte[c,3]:=gf_byte; + gf_ptr[c]:=gf_signed_quad; {the |>0| values will mark existing glyphs} +@!debug + print_ln(' k=',k:1,' gfptr=',gf_ptr[c]:1); +gubed@/ + k:=no_op; + end; +until k<>no_op; + +@ Here is the main information we glean from the postamble together with +some auxiliary parameters. + +@<Glob...@>= +@!design_size: integer; +@!hppp, @!vppp: integer; +@!check_sum: integer; +@!gf_post_loc: integer; +@!magnification: real; +@!dx: array [0..max_char_no] of integer; +@!dy: integer; {not used since value should always be zero} +@!total_glyphs:integer; {the total number of glyphs stored in |mm_store|} +@!mm_store:packed array [0..m1_max,4..m2_max] of eight_bits; + {to store glyph information} +@!mm,@!m1,@!m2:integer; {indices for |mm_store|} +@!free_limit:integer; {|mm| value of last free location in |mm_store|} +@!data_start:array [0..max_fonts] of integer; {|data_base+bc| for fonts} +@!font_order:array [0..max_fonts] of integer; {font numbers in loaded order} +@!gf_prev_ptr: integer; {location of next character} +@!char_code: integer; {current character number} +@!glyph_ptr: array[0..max_glyphs] of integer; + {pointers to |mm_store|} +@!max_m,@!min_m,@!max_n,@!min_n: integer; {raster bounding parameters} +@!row_count: integer; {used to correct the raster height figure} +@!column_count:integer; {used to accumulate column counts} +@!max_column_count:integer; {used to correct the raster width figure} + +@ @<Set init...@>= +for i:=0 to max_glyphs do glyph_ptr[i]:=-1; + {mark glyphs as not being in the file} +total_glyphs:=0; +mm:=4; m1:=0; m2:=4; {|-4<mm<4| saved for signalling purposes} +free_limit:=mm_max; +for i:=0 to max_fonts do font_order[i]:=-1; + +@ A temporary procedure. + +@p +@!debug +procedure tabulate; +var i,j,k,l:integer; +begin +print_nl; +print_ln(' Contents of the glyph ptr table'); +print(' '); +for j:=0 to 9 do print(j:7); +print_nl; +for i:=0 to 29 do + begin + print(i*10:3,' '); + for j:=0 to 9 do + begin + k:=glyph_ptr[10*i+j]; + l:=k div m2_size; + k:=k mod m2_size; + print(l:1,',',k:1); + end; + print_nl; + end; +end; +gubed + +@ Here is the long awaited |in_gf| routine. + +@p function in_gf(@!z:integer):boolean; {input \.{GF} data or return |false|} +label done,restart, + 9997, {go here when the format is bad} + 9998, {go here when the information cannot be loaded} + 9999; {go here to exit} +var k:integer; {index for loops} +@!nw:integer; {number of words in the width table} +@!wp:0..max_glyphs; {new value of |width_ptr| after successful input} +@!alpha,@!beta:integer; {quantities used in the scaling computation} +@!c: integer; { used it index character number} +@!o:integer; {used to hold |gf| commands} +@!p:integer; {used to hold |gf| parameter} +@!del_m:integer; {used to hold |gf| parameter} +@!del_n:integer; {used to hold |gf| parameter} +@!mm_save,@!m1_save,@!m2_save:integer; {temp save to allow for corrections} +begin +if not find_gf_postamble then + begin print_ln(' Trouble with postamble'); + goto 9997; + end; +read_gf_postamble; +@<Check |width| table and |goto 9997| if there is a problem@>; +@<Convert and store the width values@>; +@<Process the gf preamble@>; +@<Stow all of the glyph-raster info@>; +@!debug +tabulate; {Used to show the start of the |glyph_ptr| array} +print_ln(' glyph-raster done'); +gubed@/ +width_ptr:=wp; +in_gf:=true; goto 9999; +9997: print_ln('---not loaded, GF file is bad'); +@.GF file is bad@> +9998: in_gf:=false; +9999: end; + +@ @<Check |width| table and...@>= +font_bc[cur_font]:=bc; font_ec[cur_font]:=ec; +if font_ec[cur_font]<font_bc[cur_font] then + font_bc[cur_font]:=font_ec[cur_font]+1; +if width_ptr+font_ec[cur_font]-font_bc[cur_font]+1>max_glyphs then + begin print_ln('---not loaded, DVIIMP needs larger width table'); + goto 9998; + end; +wp:=width_ptr+font_ec[cur_font]-font_bc[cur_font]+1; +nw:=ec+1-bc; +@!debug +print_ln(' bc=',bc:1,' ec=',ec:1,' nw=',nw:1); +gubed@/ +if (nw=0)or(nw>256) then goto 9997; + +@ @<Process the gf preamble@>= +open_gf_file; +o:=gf_byte; {fetch the first byte} +if o<>pre then begin + print_ln(' GF file does not start with |pre|'); + goto 9997; + end; +o:=gf_byte; {fetch the identification byte} +if o<>gf_id_byte then begin + print_ln(' id =',o:1,' should be ',gf_id_byte:1); + goto 9997; + end; +o:=gf_byte; {fetch the length of the introductory comment} +while o>0 do + begin decr(o); p:=gf_byte; + end; + +@ An important part of |in_gf| is the width computation, which +involves multiplying the relative widths in the \.{GF} file by the +scaling factor in the \.{DVI} file. This fixed-point multiplication +must be done with precisely the same accuracy by all \.{DVI}-reading programs, +in order to validate the assumptions made by \.{DVI}-writing programs +like \TeX82. + +Let us therefore summarize what needs to be done. Each width in a \.{GF} +file appears as a four-byte quantity called a |fix_word|. A |fix_word| +whose respective bytes are $(a,b,c,d)$ represents the number +$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr +b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr +-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$ +(No other choices of $a$ are allowed, since the magnitude of a \.{GF} +dimension must be less than 16.) We want to multiply this quantity by the +integer~|z|, which is known to be less than $2^{27}$. Let $\alpha=16z$. +If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$, +$d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or +16, to obtain a multiplier less than $2^{23}$, and we can compensate for +this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let +$\beta=2^{4-e}$; we shall compute +$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$, +or the same quantity minus $\alpha$ if $a=255$. This calculation must be +done exactly, for the reasons stated above; the following program does the +job in a system-independent way, assuming that arithmetic is exact on +numbers less than $2^{31}$ in magnitude. + +Whereas \.{DVItype} obtained the |pixel_width|s by rounding the |width| +value, we obtain these values from the |dx| parameter associated with the +|char_loc| command. It should be noted that |width[k]| is the +device-independent width of some character in \.{DVI} units while +|pixel_width[k]| is the corresponding pixel width of that character in an +actual font. + +The macro |char_pixel_width| is set up to be analogous to |char_width|. + +@d char_pixel_width(#)==pixel_width[data_base[#]+char_width_end + +@d pixel_round(#)==round(conv*(#)) + +@<Glob...@>= +@!pixel_width:array[0..max_glyphs] of integer; {actual character widths, + in pixels} +@!conv:real; {converts \.{DVI} units to pixels} +@!true_conv:real; {converts unmagnified \.{DVI} units to pixels} +@!numerator,@!denominator:integer; {stated conversion ratio} +@!mag:integer; {magnification factor times 1000} +@!empty_glyph: boolean; {foxing Imagen into accepting an empty glyph} + +@ @<Convert and store the width values@>= +@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>; +data_base[cur_font]:=width_ptr-bc; +data_start[cur_font]:=width_ptr; +wp:=width_ptr+ec-bc+1; +c:=bc; +for k:=width_ptr to wp-1 do begin + if gf_ptr[c]=0 then begin + width[k]:=invalid_width; pixel_width[k]:=0; +@!debug + print(' invalid width for c=',c:1); +gubed + end + else begin + width[k]:=(((((w_byte[c,3]*z)div@'400) + +(w_byte[c,2]*z))div@'400)+(w_byte[c,1]*z))div beta; + if (w_byte[c,0]>0) then + if (w_byte[c,0]<255) then begin + print_ln(' w byte=',w_byte[c,0]:1); + goto 9997 + end + else width[k]:=width[k]-alpha; + pixel_width[k]:=dx[c]; + end; +@!debug + print(' dx=',dx[c]:1,' for ',c:1); + print(' [ ',c:1,']'); +gubed@/ + incr(c); + end; + +@ @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>= +begin alpha:=16; +while z>=@'40000000 do + begin z:=z div 2; alpha:=alpha+alpha; + end; +beta:=256 div alpha; alpha:=alpha*z; +end + + +@ In those few cases (we hope) where a \.{GF} file is not available we +will want to refer to the \.{TFM} file and leave space in the document for +the missing glyphs. The following procedure is used for this purpose. + +@p function in_tfm(@!z:integer):boolean; {input \.{TFM} data or return |false|} +label 9997, {go here when the format is bad} + 9998, {go here when the information cannot be loaded} + 9999; {go here to exit} +var k:integer; {index for loops} +@!lh:integer; {length of the header data, in four-byte words} +@!nw:integer; {number of words in the width table} +@!wp:0..max_glyphs; {new value of |width_ptr| after successful input} +@!alpha,@!beta:integer; {quantities used in the scaling computation} +begin @<Read past the header data; |goto 9997| if there is a problem@>; +@<Store character-width indices at the end of the |width| table@>; +@<Read and convert the width values, setting up the |in_width| table@>; +@<Move the widths from |in_width| to |width|, + and append |pixel_width| values@>; +width_ptr:=wp; in_tfm:=true; goto 9999; +9997: print_ln('---not loaded, TFM file is bad'); +@.TFM file is bad@> +9998: in_tfm:=false; +9999: end; + +@ @<Read past the header...@>= +read_tfm_word; lh:=b2*256+b3; +read_tfm_word; font_bc[cur_font]:=b0*256+b1; +font_ec[cur_font]:=b2*256+b3; +if font_ec[cur_font]<font_bc[cur_font] then + font_bc[cur_font]:=font_ec[cur_font]+1; +if width_ptr+font_ec[cur_font]-font_bc[cur_font]+1>max_glyphs then + begin print_ln('---not loaded, DVItype needs larger width table'); +@.DVItype needs larger...@> + goto 9998; + end; +wp:=width_ptr+font_ec[cur_font]-font_bc[cur_font]+1; +read_tfm_word; nw:=b0*256+b1; +if (nw=0)or(nw>256) then goto 9997; +for k:=1 to 3+lh do + begin if eof(tfm_file) then goto 9997; + read_tfm_word; + if k=4 then + if b0<128 then tfm_check_sum:=((b0*256+b1)*256+b2)*256+b3 + else tfm_check_sum:=(((b0-256)*256+b1)*256+b2)*256+b3; + end; + +@ @<Store character-width indices...@>= +if wp>0 then for k:=width_ptr to wp-1 do + begin read_tfm_word; + if b0>nw then goto 9997; + width[k]:=b0; + end; + +@ @<Read and convert the width values...@>= +@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>; +for k:=0 to nw-1 do + begin read_tfm_word; + in_width[k]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta; + if b0>0 then if b0<255 then goto 9997 + else in_width[k]:=in_width[k]-alpha; + end + +@ @<Move the widths from |in_width| to |width|, + and append |pixel_width| values@>= +if in_width[0]<>0 then goto 9997; {the first width should be zero} +data_base[cur_font]:=width_ptr-font_bc[cur_font]; +if wp>0 then for k:=width_ptr to wp-1 do + if width[k]=0 then + begin width[k]:=invalid_width; pixel_width[k]:=0; + end + else begin width[k]:=in_width[width[k]]; + pixel_width[k]:=pixel_round(width[k]); + glyph_ptr[k]:=-1; + end + +@ @<Stow all...@>= +@!debug +print_ln(' loading font ',cur_font:1,'[',m1:1,',',m2:1,'] '); +print((free_limit div m2_size):1,'-',(free_limit mod m2_size):1,' '); +gubed@/ +k:=0; while font_order[k]>=0 do incr(k); +font_order[k]:=cur_font; {add this font to ordered list} +repeat gf_prev_ptr:=cur_gf_loc; +@<Pass |no_op|, |xxx| and |yyy| commands@>; +if (o=boc) or (o=boc1) then + begin + m1_save:=m1; m2_save:=m2; {for width and height corrections} + mm_save:=m1*m2_size+m2; {to be stored in |glyph_ptr| when |c| is known} + if o=boc then begin @<Stow the |boc| information@> end + else begin @<Stow the |boc1| information@>; + end; +glyph_ptr[data_base[cur_font]+c]:=mm_save; {save glyph start address} +@!debug +print(' (',cur_font:1,')',c:1,'[',m1:1,',',m2:1,']'); +gubed@/ +if empty_glyph then + begin + glyph_ptr[data_base[cur_font]+c]:=-1; + empty_glyph:=false; + end; + @<Stow the glyph details@>; + end; +until o=post; + +@ As noted earlier, the parameters associated with the |boc| command are +received from the |gf| file as |signed_quad|s and are converted into the +form needed by the \.{IMAGEN} and then stowed into |mm_store| as +|signed_pairs|, in keeping with the restricted range of value that the +\.{IMAGEN} allows. + +@ @<Stow the |boc| information@>= +incr(total_glyphs); +char_code:=gf_signed_quad; +p:=gf_signed_quad; +c:=char_code mod 256; +if c<0 then c:=c+256; +if c>127 then if im_extension[cur_font]=-1 then + begin + if nf2=nf then + begin + print_ln(' ---Out of font storage space'); + goto 9998; + end; + im_extension[cur_font]:=nf2; decr(nf2); + end; +@!debug +print(' boc[',c:1,']'); +if char_code<>c then + print(' in family ',(char_code-c) div 256 : 1); +gubed@/ +min_m:=gf_signed_quad; max_m:=gf_signed_quad; +min_n:=gf_signed_quad; max_n:=gf_signed_quad; +if max_m-min_m<=0 then empty_glyph:=true else empty_glyph:=false; +stow_signed_pair(max_m-min_m+1); {width} +stow_signed_pair(-min_m); {left offset} +stow_signed_pair(max_n-min_n+1); {height} +stow_signed_pair(max_n); {top offset} + +@ Similarly, the one byte parameters associated with the +|boc1| command are converted into the required form and stored into +|mm_store| as |signed_pairs|. + +@ @<Stow the |boc1| information@>= +incr(total_glyphs); +char_code:=gf_byte; +p:=-1; +c:=char_code; +if c>127 then if im_extension[cur_font]=-1 then + begin + if nf2=nf then + begin + print_ln(' ---Out of font storage space'); + goto 9998; + end; + im_extension[cur_font]:=nf2; decr(nf2); + end; +@!debug +print_nl; print_nl; +print(' boc1[',c:1,']'); +gubed@/ +del_m:=gf_byte; max_m:=gf_byte; +del_n:=gf_byte; max_n:=gf_byte; +if del_m<=0 then empty_glyph:=true else empty_glyph:=false; +stow_signed_pair(del_m+1); +stow_signed_pair(del_m-max_m); +stow_signed_pair(del_n+1); +stow_signed_pair(max_n); +@!debug +print_ln(' c=',c:1,' del_m+1=',del_m+1:1,' del_m-max_m=',del_m-max_m:1, +' del_n+1=',del_n+1:1,' max_n=',max_n:1); +gubed@/ + +@ Having deciphered a |boc| command or a |boc1| command and having stored +the necessary information that precedes the mask information in a |bgly| +command, we can limit the variety of commands that are to be stored to +only those commands actually needed to specify the mask portion of a +|bgly| command. + +@ @<Pass |no_op|, |xxx| and |yyy| commands@>= +repeat + o:=gf_byte; + if (o=yyy) then begin + p:=first_gf_par(o); o:=no_op; + end + else if (o>=xxx1) and (o<=xxx1+3) then begin + p:=first_gf_par(o); + while p>0 do begin q:=gf_byte; decr(p); end; + o:=no_op; + end; +until o<>no_op; + +@ @<Stow the glyph details@>= +max_column_count:=0; {set for the glyph} +column_count:=0; +row_count:=0; +while true do begin + restart: + o:=gf_byte; + case o of + sixty_four_cases(paint_0): begin + column_count:=column_count+o-paint_0; +{|print_ln(' s0 ',o:1);|} + end; + paint1: begin + stow(o); o:=gf_byte; column_count:=column_count+o; +{|print_ln(' s1 ',o:1);|} + end; + paint2: begin + stow(o); o:=gf_byte; + stow(o); column_count:=column_count+256*o; + o:=gf_byte; column_count:=column_count+o; + end; + skip0: begin + incr(row_count); + if column_count>max_column_count then + max_column_count:=column_count; + column_count:=0; + end; + skip1: begin + stow(o); o:=gf_byte; + row_count:=row_count+1+o; + if column_count>max_column_count then + max_column_count:=column_count; + column_count:=0; + end; + one_sixty_five_cases(new_row_0): begin + incr(row_count); + if column_count>max_column_count then + max_column_count:=column_count; + column_count:=o-new_row_0; + end; + xxx1: begin + o:=gf_byte; + while o>0 do begin q:=gf_byte; decr(o); end; + goto restart; + end; + yyy: begin + o:=5; + while o>0 do begin q:=gf_byte; decr(o); end; + goto restart; + end; + no_op: goto restart; + eoc: goto done; + othercases + print_ln('! Unexpected command: ',o:1) + endcases; + stow(o); + end; +done: +stow(o); {this should be an |eoc| command} +{|print_ln('S EOC');|} +if column_count>0 then incr(row_count); {last row isn't terminated} +if column_count>max_column_count then max_column_count:=column_count; +mm_store[m1_save,m2_save]:=max_column_count div 256; +if m2_save<m2_max then incr(m2_save) else + begin m2_save:=4; if m1_save<m1_max then incr(m1_save) + else m1_save:=0; + end; +mm_store[m1_save,m2_save]:=max_column_count mod 256; +if m2_save+3<m2_size then m2_save:=m2_save+3 else + begin m2_save:=m2_save+7-m2_size; if m1_save<m1_max then incr(m1_save) + else m1_save:=0; + end; +mm_store[m1_save,m2_save]:=row_count div 256; +if m2_save<m2_max then incr(m2_save) else + begin m2_save:=4; if m1_save<m1_max then incr(m1_save) + else m1_save:=0; + end; +mm_store[m1_save,m2_save]:=row_count mod 256; + +@* Optional modes of output. +As normally compiled, the |dialog| routine is not called and \.{DVIIMP} +operated in the |errors_only| mode. One can remove the brackets ( {|...|} ) +that surround the |dialog| call in the main program module and +\.{DVIIMP} will then print different quantities of information based on some +options that the user must specify: The |out_mode| level is set to one of +four values (|errors_only|, |terse|, |verbose|, |the_works|), giving +different degrees of output; and the typeout can be confined to a +restricted subset of the pages by specifying the desired starting page and +the maximum number of pages. Furthermore there is an option to specify the +resolution of an assumed discrete output device, so that pixel-oriented +calculations will be shown; and there is an option to override the +magnification factor that is stated in the \.{DVI} file. + +The starting page is specified by giving a sequence of 1 to 10 numbers or +asterisks separated by dots. For example, the specification `\.{1.*.-5}' +can be used to refer to a page output by \TeX\ when $\.{\\count0}=1$ +and $\.{\\count2}=-5$. (Recall that |bop| commands in a \.{DVI} file +are followed by ten `count' values.) An asterisk matches any number, +so the `\.*' in `\.{1.*.-5}' means that \.{\\count1} is ignored when +specifying the first page. If several pages match the given specification, +\.{DVIIMP} will begin with the earliest such page in the file. The +default specification `\.*' (which matches all pages) therefore denotes +the page at the beginning of the file. + +When the modified \.{DVIIMP} begins, it engages the user in a brief dialog +so that the options will be specified. This part of \.{DVIIMP} requires +nonstandard \PASCAL\ constructions to handle the online interaction; so it +may not be easy to allow for this dialog, +and if so, one should simply to stick to the +default options (starting page `\.*' (but printed in +reverse order), +|max_pages=1000|, |resolution=300.0|, |new_mag=0|). On other hand, the +system-dependent routines that are needed are not complicated, so it should +not be terribly difficult to introduce them. +@^system dependencies@> + +@<Glob...@>= +@!max_pages:integer; {at most this many |bop..eop| pages will be printed} +@!resolution:real; {pixels per inch} +@!new_mag:integer; {if positive, overrides the postamble's magnification} + +@ The starting page specification is recorded in two global arrays called +|start_count| and |start_there|. For example, `\.{1.*.-5}' is represented +by |start_there[0]=true|, |start_count[0]=1|, |start_there[1]=false|, +|start_there[2]=true|, |start_count[2]=-5|. +We also set |start_vals=2|, to indicate that count 2 was the last one +mentioned. The other values of |start_count| and |start_there| are not +important, in this example. + +@<Glob...@>= +@!start_count:array[0..9] of integer; {count values to select starting page} +@!start_there:array[0..9] of boolean; {is the |start_count| value relevant?} +@!start_vals:0..9; {the last count considered significant} +@!count:array[0..9] of integer; {the count values on the current page} + +@ @<Set init...@>= +max_pages:=1000; start_vals:=0; start_there[0]:=false; new_mag:=0; + +@ Here is a simple subroutine that tests if the current page might be the +starting page. + +@p function start_match:boolean; {does |count| match the starting spec?} +var k:0..9; {loop index} +@!match:boolean; {does everything match so far?} +begin match:=true; +for k:=0 to start_vals do + if start_there[k]and(start_count[k]<>count[k]) then match:=false; +start_match:=match; +end; + +@ The |input_ln| routine waits for the user to type a line at his or her +terminal; then it puts ASCII-code equivalents for the characters on that line +into the |buffer| array. The |term_in| file is used for terminal input, +and |term_out| for terminal output. +@^system dependencies@> + +@<Glob...@>= +@!buffer:array[0..terminal_line_length] of ASCII_code; +@!term_in:text_file; {the terminal, considered as an input file} +@!term_out:text_file; {the terminal, considered as an output file} + +@ Since the terminal is being used for both input and output, some systems +need a special routine to make sure that the user can see a prompt message +before waiting for input based on that message. (Otherwise the message +may just be sitting in a hidden buffer somewhere, and the user will have +no idea what the program is waiting for.) We shall invoke a system-dependent +subroutine |update_terminal| in order to avoid this problem. +@^system dependencies@> + +@d update_terminal == break(term_out) {empty the terminal output buffer} + +@ During the dialog, \.{DVIIMP} will treat the first blank space in a +line as the end of that line. Therefore |input_ln| makes sure that there +is always at least one blank space in |buffer|. +@^system dependencies@> + +@p procedure input_ln; {inputs a line from the terminal} +var k:0..terminal_line_length; +begin update_terminal; reset(term_in); +if eoln(term_in) then read_ln(term_in); +k:=0; +while (k<terminal_line_length)and not eoln(term_in) do + begin buffer[k]:=xord[term_in^]; incr(k); get(term_in); + end; +buffer[k]:=" "; +end; + +@ The global variable |buf_ptr| is used while scanning each line of input; +it points to the first unread character in |buffer|. + +@<Glob...@>= +@!buf_ptr:0..terminal_line_length; {the number of characters read} + +@ Here is a routine that scans a (possibly signed) integer and computes +the decimal value. If no decimal integer starts at |buf_ptr|, the +value 0 is returned. The integer should be less than $2^{31}$ in +absolute value. + +@p function get_integer:integer; +var x:integer; {accumulates the value} +@!negative:boolean; {should the value be negated?} +begin if buffer[buf_ptr]="-" then + begin negative:=true; incr(buf_ptr); + end +else negative:=false; +x:=0; +while (buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9") do + begin x:=10*x+buffer[buf_ptr]-"0"; incr(buf_ptr); + end; +if negative then get_integer:=-x @+ else get_integer:=x; +end; + +@ The selected options are put into global variables by the |dialog| +procedure, which is called just as \.{DVIIMP} begins. +@^system dependencies@> + +@p procedure dialog; +label 2,3,4,5; +var k:integer; {loop variable} +begin rewrite(term_out); {prepare the terminal for output} +write_ln(term_out,banner); +@<Determine the desired |start_count| values@>; +@<Determine the desired |max_pages|@>; +@<Determine the desired |resolution|@>; +@<Determine the desired |new_mag|@>; +@<Print all the selected options@>; +end; + +@ @<Determine the desired |start...@>= +2: write(term_out,'Starting page (default=*): '); +start_vals:=0; start_there[0]:=false; +input_ln; buf_ptr:=0; k:=0; +if buffer[0]<>" " then + repeat if buffer[buf_ptr]="*" then + begin start_there[k]:=false; incr(buf_ptr); + end + else begin start_there[k]:=true; start_count[k]:=get_integer; + end; + if (k<9)and(buffer[buf_ptr]=".") then + begin incr(k); incr(buf_ptr); + end + else if buffer[buf_ptr]=" " then start_vals:=k + else begin write(term_out,'Type, e.g., 1.*.-5 to specify the '); + write_ln(term_out,'first page with \count0=1, \count2=-5.'); + goto 2; + end; + until start_vals=k + +@ @<Determine the desired |max_pages|@>= +3: write(term_out,'Maximum number of pages (default=1000000): '); +max_pages:=1000000; input_ln; buf_ptr:=0; +if buffer[0]<>" " then + begin max_pages:=get_integer; + if max_pages<=0 then + begin write_ln(term_out,'Please type a positive number.'); + goto 3; + end; + end + +@ @<Determine the desired |resolution|@>= +4: write(term_out,'Assumed device resolution'); +write(term_out,' in pixels per inch (default=300/1): '); +resolution:=300.0; input_ln; buf_ptr:=0; +if buffer[0]<>" " then + begin k:=get_integer; + if (k>0)and(buffer[buf_ptr]="/")and + (buffer[buf_ptr+1]>"0")and(buffer[buf_ptr+1]<="9") then + begin incr(buf_ptr); resolution:=k/get_integer; + end + else begin write(term_out,'Type a ratio of positive integers;'); + write_ln(term_out,' (1 pixel per mm would be 254/10).'); + goto 4; + end; + end + +@ @<Determine the desired |new_mag|@>= +5: write(term_out,'New magnification (default=0 to keep the old one): '); +new_mag:=0; input_ln; buf_ptr:=0; +if buffer[0]<>" " then + if (buffer[0]>="0")and(buffer[0]<="9") then new_mag:=get_integer + else begin write(term_out,'Type a positive integer to override '); + write_ln(term_out,'the magnification in the DVI file.'); + goto 5; + end + +@ After the dialog is over, we print the options so that the user +can see what \.{DVIIMP} thought was specified. + +@<Print all the selected options@>= +print_ln('Options selected:'); +@.Options selected@> +print(' Starting page = '); +for k:=0 to start_vals do + begin if start_there[k] then print(start_count[k]:1) + else print('*'); + if k<start_vals then print('.') + else print_ln(' '); + end; +print_ln(' Maximum number of pages = ',max_pages:1); +print_ln(' Resolution = ',resolution:12:8,' pixels per inch'); +if new_mag>0 then print_ln(' New magnification factor = ',new_mag/1000:8:3) + +@* Identifying and loading fonts. +\.{DVIIMP} stores the raster information relating to the glyphs that it +uses in a large |mm_store| array and stores the location of these rasters +and information relating to their state in a |glyph_ptr| array. Additional +|width| and |pixel_width| information is stored in still other arrays. + +It is usually not possible to provide a large enough |mm_store| space +for all of the fonts that may be used in some documents. \.{DVIIMP} +provides the facility for removing fonts from |mm_store| to make space for +additional fonts and then for restoring the removed fonts if this becomes +necessary. + +The general procedure is to +read the \.{DVI} postamble first to get the desired |fnt_def1| +information and to store this identifying information initially without +storing the font rasters. An array |font_state[f]| is used to keep a +record of the state of all fonts with the values set to 0 when the font +identifying information is read. Later, when a |fnt_num| command is +encountered in the body of the \.{DVI} file, the rasters for the entire +font are read in and the |font_state| value for this font is changed to 1. +However, glyphs are only downloaded as they are needed for the first time. + +The location and state for each individual glyph in all the fonts used is +kept in the |glyph_ptr| array. This array is initially set to -1, +indicating that the referenced glyphs either do not exist or that they +have not yet been read into the |mm_store| memory. The individual glyph +pointers are then set to positive values (actually, greater than 3) when +the font rasters are read in, recording the position in the |mm_store| +where the glyph is stored. These numbers are negated when each individual +glyph is downloaded. Finally, if it becomes necessary to remove rasters +to make space for other fonts, the positive |glyph_ptr| values for all +glyphs of the removed fonts are set to zero without touching the negative +pointer values (which still indicate the downloaded or non-existant states +of the glyphs in question). + +Removing the rasters for the downloaded glyphs does not in any way prevent the +continued use of these particular glyphs and no effort is made to reload any +particular font until a request is encountered for a removed non-down-loaded +glyph, as signalled by encountering a 0 value in the |glyph_ptr| array. At +this time, only the non-down-loaded glyphs of the reloaded font are restored, +with a possible substantial reduction in the space requirements as compared +with the font's initial needs, since most of the more commonly used +glyphs may have already been downloaded. + +A number of different utility procedures and functions will be needed. + +@<Glob...@>= +@!font_state:array[0..max_fonts] of integer; {0 unloaded, 1 loaded} +@!font_a_val:array[0..max_fonts] of integer; {length of directory name} +@!font_l_val:array[0..max_fonts] of integer; {length of font name} +@!scale_val:array [0..12] of integer; {table of preferred font scale values} + +@ @<Set init...@>= +scale_val[0]:=round(1.0954*resolution); +jj:=1.0; +for i:=1 to 7 do + begin jj:=1.2*jj; scale_val[i]:=round(jj*resolution); +@!debug + print_ln(' i=',i:1,' jj=',jj:1,' scale val=',scale_val[i]:1); +gubed + end; +scale_val[8]:=4*round(resolution); + {magnifications of 4000 and 5000 are sometines used} +scale_val[9]:=5*round(resolution); +scale_val[10]:=6*round(resolution); +scale_val[11]:=7*round(resolution); +scale_val[12]:=8*round(resolution); + +@ A minor problem in specifying the sizes of scaled fonts arises because +of the fact that \.{\\magstep} definitions are in terms of the rounded +values based on the magnification times 1000. For example, one will get +different values for 1)~a magnification of 1200 as applied to a font +scaled \.{\\magstep4}, and for 2)~a magnification of 1000 as applied to a +font scaled \.{\\magstep5}. The following table and function provides the +mechanism for resolving these differences by identifying the nearest match +in terms of the overall actual magnification times the resolution. At +\.{SAIL}, this figure is used as the file-name extension for standard +\.{GF} files. @^system dependencies@> + +@p function reconcile_scale(m:integer):integer; +label done; +var i:0..12; +begin +reconcile_scale:=m; +for i:=0 to 12 do + if abs(m-scale_val[i]) < abs(m-scale_val[i+1]) then + begin + if abs(m-scale_val[i])<4 then reconcile_scale:=scale_val[i]; + goto done; + end; +done: end; + +@ The following subroutine does the necessary things when a \\{fnt\_def} +command is being processed in the postamble. + +@p procedure identify_font(@!e:integer); {|e| is an external font number} +var f:0..max_fonts; +@!p:integer; {length of the area/directory spec} +@!n:integer; {length of the font name proper} +@!c,@!q,@!d:integer; {check sum, scaled size, and design size} +@!k:0..name_size; {indices into |names|} +@!m: integer; {available for use in |mag| effect caculations} +begin if nf=max_fonts then abort('DVIIMP capacity exceeded (max fonts=', + max_fonts:1,')!'); +@.DVIIMP capacity exceeded...@> +font_num[nf]:=e; f:=0; +while font_num[f]<>e do incr(f); +@<Read the font parameters into position for font |nf|@>; +@<Verify |font_scaled_size| and |font_design_size| for size@>; +font_state[nf]:=0; {font identified but not read in} +font_space[nf]:=q div 6; {this is a 3-unit ``thin space''} +incr(nf); {signalling completion of identification} +font_space[nf]:=0; {for |out_space| and |out_vmove|} +end; + +@ @<Read the font parameters into position for font |nf|...@>= +c:=signed_quad; font_check_sum[nf]:=c;@/ +q:=signed_quad; font_scaled_size[nf]:=q;@/ +d:=signed_quad; font_design_size[nf]:=d;@/ +p:=get_byte; font_a_val[nf]:=p;@/ +n:=get_byte; font_l_val[nf]:=n;@/ +if font_name[nf]+n+p>name_size then + abort('DVIIMP capacity exceeded (name size=',name_size:1,')!'); +@.DVIIMP capacity exceeded...@> +font_name[nf+1]:=font_name[nf]+n+p; +if n+p=0 then abort(' null n+p ') +@.null n+p@> +else for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=get_byte; +m:=round((0.3*mag*q)/d); +if (m>=round(1.05*resolution)) and (m<1500) then m:=reconcile_scale(m); +font_m_val[nf]:=m; +@!debug +incr(nf); +print_font(nf-1); +print('.',m:1,' '); +print_ln(' e=',e:1,' f=',nf:1,' c=',c:1,' q=',q:1,' d=',d:1, + ' p=',p:1,' n=',n:1); +decr(nf); +gubed + +@ @<Verify |font_scaled_size| and |font_design_size| for size@>= +if (q<=0)or(q>=@'1000000000) then + print('---may not load, bad scale (',q:1,')!') +@.bad scale@> + else if (d<=0)or(d>=@'1000000000) then + print('---may not load, bad design size (',d:1,')!'); +@.bad design size@> + +@ It will be desirable to skip over the |fnt_def1| commands that are found in +the body of the \.{DVI} file as our method of reading the pages in reverse +order makes it impractical for us to use them. + +@p procedure skip_it; {to bypass the |fnt_def1| commands in the body} +var i,j,k: integer; +begin +for i:=1 to 13 do j:=get_byte; +j:=j+get_byte; +if j>0 then for i:=1 to j do k:=get_byte; +end; + +@ We will have occasion to call the following from two different locations. + +@p procedure get_gf_file; +var +@!p:integer; {length of the area/directory spec} +@!n:integer; {length of the font name proper} +@!r:0..name_length; {index into |cur_name|} +@!k:0..name_size; {indices into |names|} +@!m: integer; {available for use in |mag| effect caculations} +begin +m:=font_m_val[cur_font]; +p:=font_a_val[cur_font]; +n:=font_l_val[cur_font]; +@<Move font name into the |cur_name| string@>; +@!debug +print_font(cur_font); print('.',m:1); +print('(',cur_font:1,') '); +gubed@/ +open_gf_file; + +if eof(gf_file) then + begin + print_nl; + print_font(cur_font); print_extension(m); +@!debug + print('(',cur_font:1,') '); +gubed@/ + print(' not found'); + @<Move font name into the |cur_tfm_name| string@>; + open_tfm_file; + if eof(tfm_file) then + begin + print(' and there is no |tfm| file '); + font_state[cur_font]:=-2; + end + else + begin + print(', characters will be left blank.'); + font_state[cur_font]:=2; + end; + end; +end; + +@ If |p=0|, i.e., if no font directory has been specified, \.{DVIIMP} +is supposed to use the default font directory, which is a +system-dependent place where the standard fonts are kept. +The string variable |default_directory| contains the name of this area. +@^system dependencies@> + +@d default_directory_name=='TeXGFs:' {change this to the correct name} +@d default_directory_name_length=7 {change this to the correct length} +@d dflt_tfm_directory_name=='TeXfonts:' {change this to the correct name} +@d dflt_tfm_directory_name_length=9 {change this to the correct length} + +@<Glob...@>= +@!default_directory:packed array[1..default_directory_name_length] of char; +@!dflt_tfm_directory:packed array[1..dflt_tfm_directory_name_length] of char; + +@ @<Set init...@>= +default_directory:=default_directory_name; +dflt_tfm_directory:=dflt_tfm_directory_name; + +@ The string |cur_name| is supposed to be set to the external name of the +\.{GF} file for the current font. This usually means that we need to +prepend the name of the default directory, and +to append the suffix `\.{.GF}'. Furthermore, we change lower case letters +to upper case, since |cur_name| is a \PASCAL\ string. +@^system dependencies@> + +@<Move font name into the |cur_name| string@>= +for k:=1 to name_length do cur_name[k]:=' '; +if p=0 then + begin for k:=1 to default_directory_name_length do + cur_name[k]:=default_directory[k]; + r:=default_directory_name_length; + end +else r:=0; +for k:=font_name[cur_font] to font_name[cur_font+1]-1 do + begin incr(r); + if r+4>name_length then + abort('DVIIMP capacity exceeded (max font name length=', + name_length:1,')!'); +@.DVIIMP capacity exceeded...@> + if (names[k]>="a")and(names[k]<="z") then + cur_name[r]:=xchr[names[k]-@'40] + else cur_name[r]:=xchr[names[k]]; + end; +cur_name[r+1]:='.'; cur_name[r+2]:='G'; cur_name[r+3]:='F'; +{|cur_name[r+4]:='M';|} + +@ Normally, we only need to reference the \.{GF} files. On those +occasions when no \.{GF} file is to be found we will want to obtain the +glyph widths from a \.{TFM} file. +The following module takes care of setting the external name of this +\.{TFM} file. + +@<Move font name into the |cur_tfm_name| string@>= +for k:=1 to name_length do cur_tfm_name[k]:=' '; +if p=0 then + begin for k:=1 to dflt_tfm_directory_name_length do + cur_tfm_name[k]:=dflt_tfm_directory[k]; + r:=dflt_tfm_directory_name_length; + end +else r:=0; +for k:=font_name[cur_font] to font_name[cur_font+1]-1 do + begin incr(r); + if r+4>name_length then + abort('DVIIMP capacity exceeded (max font name length=', + name_length:1,')!'); +@.DVIIMP capacity exceeded...@> + if (names[k]>="a")and(names[k]<="z") then + cur_tfm_name[r]:=xchr[names[k]-@'40] + else cur_tfm_name[r]:=xchr[names[k]]; + end; +cur_tfm_name[r+1]:='.'; cur_tfm_name[r+2]:='T'; +cur_tfm_name[r+3]:='F'; cur_tfm_name[r+4]:='M'; + +@ We now come to the routines for reloading a font that has been removed. + +@p procedure reload_font; +label done, restart; +var k:integer; {index for loops} +@!c: integer; { used it index character number} +@!o:integer; {used to hold |gf| commands} +@!p:integer; {used to hold |gf| parameter} +@!a:integer; {used to hold |gf| parameter} +@!del_m:integer; {used to hold |gf| parameter} +@!del_n:integer; {used to hold |gf| parameter} +@!mm_save,@!m1_save,@!m2_save:integer; {to allow corrections} +begin +get_gf_file; +@<Skip over the preamble@>; +@<Restow glyph rasters that have not been downloaded@>; +font_state[cur_font]:=1; {signalling that font is loaded} +end; + +@ @<Skip over the preamble@>= +o:=gf_byte; {fetch the first byte} +o:=gf_byte; {fetch the identification byte} +o:=gf_byte; {fetch the length of the introductory comment} +while o>0 do + begin decr(o); p:=gf_byte; + end; + +@ @<Restow glyph rasters that have not been downloaded@>= +k:=0; while font_order[k]>=0 do incr(k); +font_order[k]:=cur_font; {add this font to ordered list} +repeat gf_prev_ptr:=cur_gf_loc; +@<Pass |no_op|, |xxx| and |yyy| commands@>; +if (o=boc) or (o=boc1) then begin + if o=boc then @<Read the |boc| information@> + else @<Read the |boc1| information@>; +@!debug + print(' c=',c:1); +gubed + if glyph_ptr[data_base[cur_font]+c]<0 then + @<Pass over the raster details@> {glyph has been downloaded} + else begin + mm_save:=mm; m1_save:=m1; m2_save:=m2; + {for possible width and height corrections} + glyph_ptr[data_base[cur_font]+c]:=m1*m2_size+m2; + {save glyph starting address} +@!debug +print(' (',cur_font:1,')',c:1,'[',m1:1,',',m2:1,']'); +gubed@/ + @<Stow the |boc| or |boc1| information@>; + @<Stow the glyph details@>; + end; + end; +until o=post; + +@ @<Read the |boc| information@>= +begin +char_code:=gf_signed_quad; +p:=gf_signed_quad; +c:=char_code mod 256; +if c<0 then c:=c+256; +@!debug +print('[',c:1,']'); +if char_code<>c then + print(' in family ',(char_code-c) div 256 : 1); +gubed@/ +min_m:=gf_signed_quad; max_m:=gf_signed_quad; +min_n:=gf_signed_quad; max_n:=gf_signed_quad; +del_m:=max_m-min_m; +del_n:=max_n-min_n; +end + +@ @<Read the |boc1| information@>= +begin +char_code:=gf_byte; +p:=-1; +c:=char_code; +del_m:=gf_byte; max_m:=gf_byte; +del_n:=gf_byte; max_n:=gf_byte; +min_m:=max_m-del_m; +end + +@ @<Stow the |boc| or |boc1| information@>= +stow_signed_pair(del_m+1); +stow_signed_pair(-min_m); {this is the initial |m| value} +stow_signed_pair(del_n+1); +stow_signed_pair(max_n); + +@ @<Pass over the raster details@>= {this glyph has been downloaded} +begin +o:=gf_byte; +while o<>eoc do begin + a:=cur_gf_loc; + while (o<paint1) or (o=skip0) or ((o>=new_row_0) and (o<=new_row_164)) do + o:=gf_byte; + if (o=paint1) or (o=skip1) then begin + p:=gf_byte; o:=gf_byte; + end + else if (o=paint2) or (o=skip2) then begin + p:=gf_byte; p:=gf_byte; o:=gf_byte; + end + else if o=xxx1 then begin {\MF\ will not do this but it is allowed} + p:=gf_byte; + while p>0 do begin q:=gf_byte; decr(p); end; + o:=gf_byte; + end; + end; +end + +@* Downloading glyph information. +As mentioned earlier, the information for each used glyph (as stored in +the |mm_store| array) will have to be translated and downloaded by means of +an |im_bgly| command on the first occasion that the glyph is to be +printed. The following definitions and tables will assist in this work: + +@d advance_q==begin if q2<m2_max then incr(q2) + else + begin + q2:=4; {|-4<m2<4| is left free for other uses} + if q1<m1_max then incr(q1) else q1:=0; + end; + end + +@<Glob...@>= +@!atab:array[1..8] of integer; {used to locate asterisks if showing pattern} +@!btab:array[0..8] of integer; {used to define bits to blacken} + +@ @<Set initial values@>= +atab[1]:=128; +btab[0]:=255; +for i:=2 to 8 do atab[i]:=atab[i-1] div 2; +for i:=1 to 8 do btab[i]:=btab[i-1] div 2; + +@ We will also have occasion to read halfwords from |mm_store|. + +@p function read_signed_pair(mm_tmp:integer):integer; + {returns the next two bytes, signed} +var a,b:eight_bits; +m1_tmp,m2_tmp:integer; +begin +m1_tmp:=mm_tmp div (m2_size); m2_tmp:=mm_tmp mod (m2_size); +a:=mm_store[m1_tmp,m2_tmp]; +if m2_tmp<m2_max then incr(m2_tmp) +else + begin m2_tmp:=4; + if m1_tmp<m1_max then incr(m1_tmp) else m1_tmp:=0; {wrap-around assumed} + end; +b:=mm_store[m1_tmp,m2_tmp]; +if a<128 then read_signed_pair:=(a*256)+b +else read_signed_pair:=(a-256)*256+b; +end; + +@ For debugging purposes it may be desirable to display the actual glyph +raster while it is being downloaded. + +@p procedure show_it(v:integer); +var i: integer; +begin +for i:=1 to 8 do + if v>=atab[i] then + begin + print('*'); v:=v-atab[i]; + end else print('.'); +end; + +@ And here is the procedure that does the actual downloading. + +@p procedure do_im_bgly(@!c:integer); +var b,dis,n,i,q,val,w,real_w:integer; +q1,q2: integer; +bytes_required:integer; {bytes per row for current glyph} +begin +im_byte(im_bgly); +if c<128 then im_halfword(cur_font*128+c) {normal family and member name} +else im_halfword(im_extension[cur_font]*128+c-128); + {Imagen's family and member name} +q:=pixel_width[data_base[cur_font]+c]; +im_halfword(q); {advance width} +q:=glyph_ptr[data_base[cur_font]+c]; + {get starting location in |mm_store|} +q1:= q div (m2_size); q2:=q mod (m2_size); +@!debug +print(' im(',cur_font:1,')',c:1,'[',q1:1,',',q2:1,']'); +gubed@/ +bytes_required:=((read_signed_pair(q)+7)div 8); +for i:=1 to 8 do + begin + im_byte(mm_store[q1,q2]); + advance_q; + end; {width, left offset, height,top offset} +n:=0; dis:=0; val:=0; w:=0; real_w:=0; +while real_w<>eoc do begin + @<Translate a sequence of paint commands@>; + w:=mm_store[q1,q2]; + real_w:=w; + if (w>=new_row_0) and (w<=new_row_164) then + @<Translate a |new_row| command@> + else if (w>=skip0) and (w<new_row_0) then + @<Translate a |skip| command@> +else if real_w<>eoc then +print_ln('BAD D L COM ',w:1,' (',cur_font:1,')',c:1,'[',q1:1,',',q2:1,']'); + end; +{|print_ln('G EOC');|} +glyph_ptr[data_base[cur_font]+c]:=-glyph_ptr[data_base[cur_font]+c]; + {to show that the glyph has been downloaded} +end; + +@ @<Translate a sequence of paint commands@>= +while n<bytes_required do begin + if dis=0 then begin + @<Get two paint commands@>; dis:=w+b; + end; + while dis<8 do begin + val:=val+btab[w]-btab[dis]; + @<Get two paint commands@>; w:=dis+w; dis:=w+b; + end; + if w>=8 then w:=w-8 + else begin + val:=val+btab[w]; w:=0; + end; + im_byte(val); dis:=dis-8; + val:=0; incr(n); + end + +@ @<Translate a |new_row| command@>= +begin +w:=w-new_row_0; +advance_q; +b:=mm_store[q1,q2]; +if b<=paint2 then begin + advance_q; + if b=paint2 then + begin b:=mm_store[q1,q2]; advance_q; + b:=b*256+mm_store[q1,q2]; advance_q; + end + else if b=paint1 then begin + b:=mm_store[q1,q2]; advance_q; + end; + n:=0; dis:=w+b; val:=0; + end +else begin b:=0; w:=8*bytes_required; {a safety measure} + end; +n:=0; dis:=w+b; val:=0; +end + +@ @<Translate a |skip| command@>= +begin +if w>skip0 then begin + advance_q; + w:=mm_store[q1,q2]; + while w>0 do begin + for n:=1 to bytes_required do im_byte(0); + decr(w); + end; + end; +advance_q; +n:=0; dis:=0; val:=0; w:=0; b:=0; +end + +@ @<Get two paint commands@>= +begin w:=mm_store[q1,q2]; +if w<=paint2 then + begin + if w=paint2 then + begin advance_q; w:=mm_store[q1,q2]; advance_q; + w:=w*256+mm_store[q1,q2]; {can be as high as 65535} + end + else if w=paint1 then + begin advance_q; w:=mm_store[q1,q2]; {can be between 64 and 255} + end; + advance_q; + b:=mm_store[q1,q2]; + if b<=paint2 then + begin + if b=paint2 then + begin advance_q; b:=mm_store[q1,q2]; advance_q; + b:=b*256+mm_store[q1,q2]; + end + else if b=paint1 then + begin advance_q; + b:=mm_store[q1,q2]; + end; + advance_q; + end + else + begin + b:=0; w:=8*bytes_required; {a safety measure} + end; + end +else + begin b:=0; w:=8*bytes_required; {a safety measure} + end; +end + + + +@* Translation to Impress form. +The main work of \.{DVIIMP} is accomplished by the |do_page| procedure, +which produces the output for an entire page, assuming that the |bop| +command for that page has already been processed. This procedure is +essentially an interpretive routine that reads and acts on the \.{DVI} +commands. + +@ The definition of \.{DVI} files refers to six registers, +$(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units. In practice, +we also need registers |hh| and |vv|, the pixel analogs of $h$ and $v$, +since it is not always true that |hh=pixel_round(h)| or +|vv=pixel_round(v)|. We will also find it useful to have two other +registers, |hhi| and |vvi| +to hold the values that \.{IMAGEN} would automatically +assign for for the horizontal and vertical locations. + +The stack of $(h,v,w,x,y,z)$ values is represented by eight arrays +called |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|. + +@<Glob...@>= +@!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!hhi,@!vv,@!vvi:integer; {current state values} +@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack: + array [0..stack_size] of integer; {pushed down values in \.{DVI} units} +@!hhstack,@!vvstack: + array [0..stack_size] of integer; {pushed down values in pixels} +@!h_org, @!v_org: integer; {page origin} + +@ Three characteristics of the pages (their |max_v|, |max_h|, and +|max_s|) are specified in the postamble. +Only |max_s| should not be exceeded. +The postamble also specifies the total number of pages. + +@<Glob...@>= +@!max_v:integer; {the value of |abs(v)| should probably not exceed this} +@!max_h:integer; {the value of |abs(h)| should probably not exceed this} +@!max_s:integer; {the stack depth should not exceed this} +@!max_s_so_far:integer; {the record high levels} +@!total_pages:integer; {the stated total number of pages} + +@ @<Set init...@>= +max_s:=stack_size+1; +max_s_so_far:=0; + +@ Before we get into the details of |do_page|, it is convenient to +consider a simpler routine that computes the first parameter of each +opcode. In doing this, we will use some multiple-case terms that were +defined earlier. + +@p function first_par(o:eight_bits):integer; +begin case o of +sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64): + first_par:=o-set_char_0; +set1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte; +set1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes; +set1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes; +right1,w1,x1,down1,y1,z1: first_par:=signed_byte; +right1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair; +right1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio; +set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3, + fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad; +nop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0; +w0: first_par:=w; +x0: first_par:=x; +y0: first_par:=y; +z0: first_par:=z; +sixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0; +end; +end; + +@ Here is another subroutine that we need: It computes the number of +pixels in the height or width of a rule. Characters and rules will line up +properly if the sizes are computed precisely as specified here. (Since +|conv| is computed with some floating-point roundoff error, in a +machine-dependent way, format designers who are tailoring something for a +particular resolution should not plan their measurements to come out to an +exact integer number of pixels; they should compute things so that the +rule dimensions are a little less than an integer number of pixels, e.g., +4.99 instead of 5.00.) + +@p function rule_pixels(x:integer):integer; + {computes $\lceil|conv|\cdot x\rceil$} +var n:integer; +begin n:=trunc(conv*x); +if n<conv*x then rule_pixels:=n+1 @+ else rule_pixels:=n; +end; + +@ The Imagen is capable of executing a limited repartee of graphic +commands and it will be convenient to assign a set of six +\.{\\special} commands to invoke them. +We will need the following globals. + +@<Glob...@>= +@!pen_size: integer; {must be between 0 and 20 finally} +@!hh_point,@!vv_point:array[0..255] of integer; {point coordinates} +@!p_index:integer; {used to index |hh_point| and |vv_point|} +@!join_points:array[0..255] of eight_bits; {points used in a |join|} +@!vertex_count:integer; {used to index |join_points|} +@!xxx_point:array[1..6] of eight_bits; +@!xxx_join:array[1..5] of eight_bits; +@!xxx_rectangle:array[1..10] of eight_bits; +@!xxx_circle:array[1..7] of eight_bits; +@!xxx_arc:array[1..4] of eight_bits; +@!xxx_segm:array[1..5] of eight_bits; +@!xxx_ellipse:array[1..8] of eight_bits; +@!xxx_o:eight_bits; {needed in special prcedures} +@!xxx_k:integer; {needed in special prcedures} + +@ @<Set initial values@>= +xxx_point[1]:="p"; xxx_point[2]:="o"; xxx_point[3]:="i"; xxx_point[4]:="n"; +xxx_point[5]:="t"; xxx_point[6]:=" "; +xxx_join[1]:="j"; xxx_join[2]:="o"; xxx_join[3]:="i"; xxx_join[4]:="n"; +xxx_join[5]:=" "; +xxx_rectangle[1]:="r"; xxx_rectangle[2]:="e"; xxx_rectangle[3]:="c"; +xxx_rectangle[4]:="t"; xxx_rectangle[5]:="a"; xxx_rectangle[6]:="n"; +xxx_rectangle[7]:="g"; xxx_rectangle[8]:="l"; xxx_rectangle[9]:="e"; +xxx_rectangle[10]:=" "; +xxx_circle[1]:="c"; xxx_circle[2]:="i"; xxx_circle[3]:="r"; xxx_circle[4]:="c"; +xxx_circle[5]:="l"; xxx_circle[6]:="e"; xxx_circle[7]:=" "; +xxx_arc[1]:="a"; xxx_arc[2]:="r"; xxx_arc[3]:="c"; xxx_arc[4]:=" "; +xxx_segm[1]:="s"; xxx_segm[2]:="e"; xxx_segm[3]:="g"; xxx_segm[4]:="m"; +xxx_segm[5]:=" "; +xxx_ellipse[1]:="e"; xxx_ellipse[2]:="l"; +xxx_ellipse[3]:="l"; xxx_ellipse[4]:="i"; +xxx_ellipse[5]:="p"; xxx_ellipse[6]:="s"; +xxx_ellipse[7]:="e"; xxx_ellipse[8]:=" "; + +@ The following procedures will be used for these \.{\\special} commands. + +@p function read_ascii(p:integer):real; +var jj,kk:real; +negative:boolean; +begin +jj:=0.0; +negative:=false; +while (xxx_o=" ") and (xxx_k<p) do begin incr(xxx_k); xxx_o:=get_byte; end; +if (xxx_o="-") and (xxx_k<p) then + begin negative:=true; + incr(xxx_k); xxx_o:=get_byte; + end; +while (xxx_o>="0") and (xxx_o<="9") and (xxx_k<=p) do + begin + jj:=jj*10+(xxx_o-"0"); incr(xxx_k); + if xxx_k<=p then xxx_o:=get_byte; + end; +if (xxx_o=".") and (xxx_k<p) then + begin + incr(xxx_k); xxx_o:=get_byte; + kk:=1.0; + while (xxx_o>="0") and (xxx_o<="9") and (xxx_k<=p) do + begin + kk:=kk*0.1; jj:=jj+kk*(xxx_o-"0"); incr(xxx_k); + if xxx_k<=p then xxx_o:=get_byte; + end; + end; +if negative then jj:=-jj; +read_ascii:=jj; +end; + +@ This procedure defines the points for use by the |do_join| procedure +that follows. + +@p procedure do_point(p:integer); +var k:integer; {loop variable} +o:eight_bits; +match:boolean; {does everything match} +begin if p<7 then for k:=2 to p do o:=get_byte else + begin match:=true; + for k:=2 to 6 do + begin o:=get_byte; + if o<>xxx_point[k] then match:=false; +@!debug + print(xchr[o]); +gubed + end; + p_index:=0; + for k:=7 to p do + begin o:=get_byte; + if match then p_index:=p_index*10+o-"0"; + end; + if match then + begin hh_point[p_index]:=pixel_round(h); + vv_point[p_index]:=pixel_round(v); +@!debug +print(p_index:1,' ',pixel_round(h):1,',',pixel_round(v):1); +gubed + end; + end; +end; + +@ The |do_join| procedure joins points by straight lines only. + +@p procedure do_join(p:integer); +var k,q:integer; +jj:real; {used in computing |pen_size|} +match:boolean; {does everything match} +begin if p<8 then for k:=2 to p do xxx_o:=get_byte else + begin match:=true; + for k:=2 to 5 do + begin xxx_o:=get_byte; + if xxx_o<>xxx_join[k] then match:=false; + end; + if not match then for k:=6 to p do xxx_o:=get_byte else + begin xxx_o:=get_byte; + xxx_k:=6; + jj:=read_ascii(p); + pen_size:=pixel_round(jj*65536.0); + if pen_size>20 then pen_size:=20 else if pen_size<0 then pen_size:=0; + im_byte(set_pen); im_byte(pen_size); + vertex_count:=1; q:=0; incr(xxx_k); + for k:=xxx_k to p do begin + xxx_o:=get_byte; + if (xxx_o>="0") and (xxx_o<="9") then q:=q*10+xxx_o-"0" else + if xxx_o=" " then begin + join_points[vertex_count]:=q; incr(vertex_count); q:=0; + end; + end; + join_points[vertex_count]:=q; + im_byte(create_path); + im_halfword(vertex_count); + for q:=1 to vertex_count do + begin im_halfword(hh_point[join_points[q]]); + im_halfword(vv_point[join_points[q]]); + end; + im_byte(draw_path); im_byte(15); + end; + end; +end; + +@ And now we come the the |do_circle| procedure. + +@p procedure do_circle(p:integer); +var k,q,r:integer; jj:real; +match:boolean; {does everything match} +begin if p<13 then for k:=2 to p do xxx_o:=get_byte else + begin match:=true; + for k:=2 to 7 do + begin xxx_o:=get_byte; + if xxx_o<>xxx_circle[k] then match:=false; +@!debug + print(xchr[xxx_o]); +gubed + end; + if not match then for k:=8 to p do xxx_o:=get_byte else + begin xxx_o:=get_byte; + xxx_k:=8; + jj:=read_ascii(p); + pen_size:=pixel_round(jj*65536.0); + if pen_size>20 then pen_size:=20 else if pen_size<0 then pen_size:=0; + im_byte(set_pen); im_byte(pen_size); + @<Resyncronize@>; + im_byte(circ_arc); +@!debug + print('(',pen_size:1,')'); +gubed + jj:=read_ascii(p); + r:=pixel_round(jj*65536.0); im_halfword(r); {the radius} +@!debug + print('(',r:1,')'); +gubed + jj:=read_ascii(p); + q:=-round(jj*16384/360); {to measure counterclockwise} + im_halfword(q); {first angle} +@!debug + print('(',q:1,')'); +gubed + jj:=read_ascii(p); + r:=-round(jj*16384/360); {to measure counterclockwise} + im_halfword(r); {second angle} +@!debug + print('(',r:1,')'); +gubed + im_byte(draw_path); im_byte(15); + end; + end; +end; + +@ And finally the |do_ellipse| procedure. +@p procedure do_ellipse(p:integer); +var k,q,r:integer; jj:real; +match:boolean; {does everything match} +begin if p<18 then for k:=2 to p do xxx_o:=get_byte else + begin match:=true; + for k:=2 to 8 do + begin xxx_o:=get_byte; + if xxx_o<>xxx_ellipse[k] then match:=false; +@!debug + print(xchr[xxx_o]); +gubed + end; + if not match then for k:=9 to p do xxx_o:=get_byte else + begin xxx_o:=get_byte; + xxx_k:=9; + jj:=read_ascii(p); + pen_size:=pixel_round(jj*65536.0); + if pen_size>20 then pen_size:=20 else if pen_size<0 then pen_size:=0; + im_byte(set_pen); im_byte(pen_size); + @<Resyncronize@>; + im_byte(ellipse_arc); +@!debug + print('(',pen_size:1,')'); +gubed + jj:=read_ascii(p); + r:=pixel_round(jj*65536.0); im_halfword(r); {radiusa} +@!debug + print('(',r:1,')'); +gubed + jj:=read_ascii(p); + r:=pixel_round(jj*65536.0); im_halfword(r); {radiusb} +@!debug + print('(',r:1,')'); +gubed + jj:=read_ascii(p); + q:=-round(jj*16384/360); {to measure counterclockwise} + im_halfword(q); {|alpha_offset|} +@!debug + print('(',q:1,')'); +gubed + jj:=read_ascii(p); + q:=-round(jj*16384/360); {to measure counterclockwise} + im_halfword(q); {first angle} +@!debug + print('(',q:1,')'); +gubed + jj:=read_ascii(p); + r:=-round(jj*16384/360); {to measure counterclockwise} + im_halfword(r); {second angle} +@!debug + print('(',r:1,')'); +gubed + im_byte(draw_path); im_byte(15); + end; + end; +end; + +@ The |do_page| +subroutine is organized as a typical interpreter, with a multiway branch +on the command code followed by |goto| statements leading to routines that +finish up the activities common to different commands. We will use the +following labels: + +@d fin_set=41 {label for commands that set or put a character} +@d fin_rule=42 {label for commands that set or put a rule} +@d move_right=43 {label for commands that change |h|} +@d move_down=44 {label for commands that change |v|} +@d change_font=45 {label for commands that change |cur_font|} + +@ Some \PASCAL\ compilers severely restrict the length of procedure bodies, +so we shall split |do_page| into two parts, one of which is +called |special_cases|. The different parts communicate with each other +via the global variables mentioned above, together with the following ones: + +@<Glob...@>= +@!s:integer; {current stack size} +@!cur_font:integer; {current internal font number} + +@ Here is the overall setup. + +@d infinity==@'17777777777 {$\infty$ (approximately)} + +@p @t\4@>@<Declare the function called |special_cases|@>@; +procedure do_page; +label fin_set,fin_rule,move_right,done,9999; +var o:eight_bits; {operation code of the current command} +@!p,@!q:integer; {parameters of the current command} +@!g:integer; {to hold |glyph_ptr| temporarily and force its computation} +@!a:integer; {byte number of the current command} +@!hhh:integer; {|h|, rounded to the nearest pixel} +begin cur_font:=nf; {set current font undefined} + s:=0; w:=0; x:=0; y:=0; z:=0; + h:=round(h_org/conv); v:=round(v_org/conv); + hh:=pixel_round(h); vv:=pixel_round(v); + hhi:=infinity; vvi:=infinity; + {initialize the state variables} +while true do @<Translate the next command in the \.{DVI} file; + |goto 9999| if it was |eop|@>; +9999: im_byte(im_end_page); +end; + +@ The following procedure will do the actual comparing of the specified +|start_page| with values of |count[0]| and it will increment |f_count|. + +@p procedure back_count; +var @!k:0..255; {command code} +begin +move_to_byte(new_backpointer); +k:=get_byte; if k=bop then + begin + incr(f_count); + for k:=0 to 9 do count[k]:=signed_quad; + if count[0]=start_page then page_match:=true; + new_backpointer:=signed_quad; + end else new_backpointer:=-1; +end; + +@ The following routine allows us to read the pages in reverse order. + +@p procedure next_page; +var @!k:0..255; {command code} +begin +incr(counter); +move_to_byte(new_backpointer); +k:=get_byte; if k=bop then + begin + for k:=0 to 9 do count[k]:=signed_quad; + new_backpointer:=signed_quad; +@!debug + print_ln(' In next_page first_backpointer=',first_backpointer:1); +gubed + end; +if (counter>=l_count) then + begin + do_page; print('[',count[0]:1,'] '); + end; +end; + +@ The main command loop. + +@<Translate the next command...@>= +begin a:=cur_loc; +@!debug + print_nl; print(a:1,': '); +gubed +o:=get_byte; p:=first_par(o); +if eof(dvi_file) then bad_dvi('the file ended prematurely'); +@.the file ended prematurely@> +@<Start translation of command |o| and |goto| the appropriate label to + finish the job@>; +fin_set: @<Finish a command that either sets or puts a character, then + |goto move_right| or |done|@>; +fin_rule: @<Finish a command that either sets or puts a rule, then + |goto move_right| or |done|@>; +move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>; +done: +end + +@ The multiway switch in |first_par|, above, was organized by the length +of each command; the one in |do_page| is organized by the semantics. + +@<Start translation...@>= +if o<set_char_0+128 then goto fin_set +else case o of + four_cases(set1): goto fin_set; + four_cases(put1): goto fin_set; + set_rule: goto fin_rule; + put_rule: goto fin_rule; + @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@; + @t\4@>@<Cases for horizontal motion@>@; + othercases begin special_cases(o,p,a); goto done; end + endcases + +@ @<Declare the function called |special_cases|@>= +procedure special_cases(@!o:eight_bits;@!p,@!a:integer); +label change_font,move_down,done; +var q:integer; {parameter of the current command} +@!k:integer; {loop index} +@!vvv:integer; {|v|, rounded to the nearest pixel} +begin +case o of +@t\4@>@<Cases for vertical motion@>@; +@t\4@>@<Cases for fonts@>@; +four_cases(xxx1): @<Translate an |xxx| command and |goto done|@>; +pre: bad_dvi('preamble command within a page!'); +@.preamble command within a page@> +post,post_post: bad_dvi('postamble command within a page!'); +@.postamble command within a page@> +othercases bad_dvi('undefined command ',o:1,'!') +@.undefined command@> +endcases; +move_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>; +change_font: @<Finish a command that changes the current font, + then |goto done|@>; +done: +end; + +@ @<Cases for commands |nop|, |bop|, \dots, |pop|@>= +nop: goto done; +bop: bad_dvi('bop occurred before eop!'); +@.bop occurred before eop@> +eop: begin + if s<>0 then bad_dvi('stack not empty at end of page (level ', + s:1,')!'); +@.stack not empty...@> + goto 9999; + end; +push: begin + if s=max_s_so_far then + begin max_s_so_far:=s+1; + if s=max_s then bad_dvi('deeper than claimed in postamble!'); +@.deeper than claimed...@> +@.push deeper than claimed...@> + if s=stack_size then + bad_dvi('DVIIMP capacity exceeded (stack size=', + stack_size:1,')'); + end; + hstack[s]:=h; vstack[s]:=v; wstack[s]:=w; + xstack[s]:=x; ystack[s]:=y; zstack[s]:=z; + hhstack[s]:=hh; vvstack[s]:=vv; incr(s); +@!debug +print(' push(',s:1,')',hh:1,',',vv:1); +gubed + goto done; + end; +pop: begin + if s=0 then bad_dvi('POP illegal at level zero') + else begin decr(s); hh:=hhstack[s]; vv:=vvstack[s]; + h:=hstack[s]; v:=vstack[s]; w:=wstack[s]; + x:=xstack[s]; y:=ystack[s]; z:=zstack[s]; +@!debug +print(' pop(',s:1,')',hh:1,',',vv:1); +gubed + end; + goto done; + end; + +@ Rounding to the nearest pixel is best done in the manner shown here, so as +to be inoffensive to the eye: When the horizontal motion is small, like a +kern, |hh| changes by rounding the kern; but when the motion is large, |hh| +changes by rounding the true position |h| so that accumulated rounding errors +disappear. We allow a larger space in the negative direction than in +the positive one, because \TeX\ makes comparatively +large backspaces when it positions accents. + +@d out_space==if (p>=font_space[cur_font])or(p<=-4*font_space[cur_font]) then + hh:=pixel_round(h+p) + else hh:=hh+pixel_round(p); + q:=p; goto move_right + +@<Cases for horizontal motion@>= +four_cases(right1): begin out_space; end; +w0,four_cases(w1):begin w:=p; out_space; end; +x0,four_cases(x1):begin x:=p; out_space; end; + +@ Vertical motion is done similarly, but with the threshold between +``small'' and ``large'' increased by a factor of five. The idea is to make +fractions like ``$1\over2$'' round consistently, but to absorb accumulated +rounding errors in the baseline-skip moves. + +@d out_vmove==if abs(p)>=5*font_space[cur_font] then vv:=pixel_round(v+p) + else vv:=vv+pixel_round(p); + goto move_down + +@<Cases for vertical motion@>= +four_cases(down1): begin out_vmove; end; +y0,four_cases(y1): begin y:=p; out_vmove; end; +z0,four_cases(z1): begin z:=p; out_vmove; end; + +@ @<Cases for fonts@>= +sixty_four_cases(fnt_num_0): goto change_font; +four_cases(fnt1): goto change_font; +four_cases(fnt_def1): begin skip_it; goto done; end; + +@ @<Translate an |xxx| command and |goto done|@>= +begin +if p<0 then bad_dvi('string of negative length!'); +@.string of negative length@> +if p<=0 then goto done; +o:=get_byte; +case o of +"p":begin +@!debug + print_nl; print(a:1,': '); + print(' p'); +gubed + do_point(p); + end; +"j":begin +@!debug + print_nl; print(a:1,': '); + print(' j'); +gubed + do_join(p); + end; +"c":begin +@!debug + print_nl; print(a:1,': '); + print(' c'); +gubed + do_circle(p); + end; +"e":begin +@!debug + print_nl; print(a:1,': '); + print(' e'); +gubed + do_ellipse(p); + end; +othercases begin print(' othercases'); + for k:=2 to p do o:=get_byte; + end +endcases; +goto done; +end + +@ @<Resyncronize@>= +if hhi<>hh then begin +@!debug +print(' ',hhi:1,',',hh:1); +gubed + hhi:=hh; im_byte(set_abs_h); im_halfword(hh); + end; +if vvi<>vv then begin + vvi:=vv; im_byte(set_abs_v); im_halfword(vv); + end; + +@ @<Finish a command that either sets or puts a character...@>= +if p<0 then p:=255-((-1-p) mod 256) +else if p>=256 then p:=p mod 256; {width computation for oriental fonts} +@^oriental characters@>@^Chinese characters@>@^Japanese characters@> +{|if (p<font_bc[cur_font])or(p>font_ec[cur_font]) then q:=invalid_width else|} + q:=char_width(cur_font)(p); +@!debug +print_ln(' p=',p:1); +print_ln(' bc=',font_bc[cur_font]:1,' ec=',font_ec[cur_font]:1); +print(' ch',char_width(cur_font)(p):1); +print_ln(' q=',q:1); +gubed +if q=invalid_width then + begin print('character ',p:1,' invalid in font '); +@.character $c$ invalid...@> + print_font(cur_font); + if cur_font<>nf then print('!'); {font |nf| has `\.!' in its name} + end +else begin + g:=glyph_ptr[data_base[cur_font]+p]; +@!debug +if g<-3 then print(' (',cur_font:1,')',p:1); +gubed + if g=0 then begin +@!debug + print_ln(' must reload (',cur_font:1,')',p:1); +gubed + reload_font; {font must be reloaded} + g:=glyph_ptr[data_base[cur_font]+p]; + end; +@!debug +if g=-1 then print(' -1(',cur_font:1,')',p:1); +gubed + if g>3 then do_im_bgly(p); + @<Resyncronize@>; +if (font_state[cur_font]=2) or (g=-1) then + begin + hhi:=hhi+pixel_width[data_base[cur_font]+p]; + @<Resyncronize@>; + end +else + begin + if p<128 then im_byte(p) {this sets or puts p of current family} + else begin + im_byte(set_family); im_byte(im_extension[cur_font]); + im_byte(p-128); {this sets or puts glyph under its imagen name} + im_byte(set_family); im_byte(cur_font); + end; + hhi:=hhi+pixel_width[data_base[cur_font]+p]; + end; + end; +if o>=put1 then goto done; +if q=invalid_width then q:=0 +else hh:=hh+char_pixel_width(cur_font)(p); +goto move_right + +@ @<Finish a command that either sets or puts a rule...@>= +q:=signed_quad; +@<Resyncronize@>; +im_byte(im_brule); im_halfword(rule_pixels(q)); im_halfword(rule_pixels(p)); +im_halfword(rule_pixels(-p)); +if o=put_rule then goto done; +hh:=hh+rule_pixels(q); +goto move_right + +@ A sequence of consecutive rules, or consecutive characters in a fixed-width +font whose width is not an integer number of pixels, can cause |hh| to drift +far away from a correctly rounded value. \.{DVIIMP} ensures that the +amount of drift will never exceed |max_drift| pixels. + +@d max_drift=2 {we insist that abs|(hh-pixel_round(h))<=max_drift|} + +@<Finish a command that sets |h:=h+q|, then |goto done|@>= +if (h>0)and(q>0) then if h>infinity-q then + begin print('arithmetic overflow! parameter changed from ', +@.arithmetic overflow...@> + q:1,' to ',infinity-h:1); + q:=infinity-h; + end; +if (h<0)and(q<0) then if -h>q+infinity then + begin print('arithmetic overflow! parameter changed from ', + q:1, ' to ',(-h)-infinity:1); + q:=(-h)-infinity; + end; +hhh:=pixel_round(h+q); +if abs(hhh-hh)>max_drift then + begin + if hhh>hh then hh:=hhh-max_drift + else hh:=hhh+max_drift; + hhi:=hh; im_byte(set_abs_h); im_halfword(hhi); + end; +h:=h+q; +@!debug +print(' r ',hh:1,' '); +gubed +goto done + +@ @<Finish a command that sets |v:=v+p|, then |goto done|@>= +if (v>0)and(p>0) then if v>infinity-p then + begin print('arithmetic overflow! parameter changed from ', +@.arithmetic overflow...@> + p:1,' to ',infinity-v:1); + p:=infinity-v; + end; +if (v<0)and(p<0) then if -v>p+infinity then + begin print('arithmetic overflow! parameter changed from ', + p:1, ' to ',(-v)-infinity:1); + p:=(-v)-infinity; + end; +vvv:=pixel_round(v+p); +if abs(vvv-vv)>max_drift then + begin + if vvv>vv then vv:=vvv-max_drift + else vv:=vvv+max_drift; + vvi:=vv; im_byte(set_abs_v); im_halfword(vvi); + end; +v:=v+p; +@!debug +print(' d ',vv:1,' '); +gubed +goto done + +@ @<Finish a command that changes the current font...@>= +font_num[nf]:=p; cur_font:=0; +while font_num[cur_font]<>p do incr(cur_font); +if cur_font=nf then bad_dvi('bad font?'); +if font_state[cur_font]=0 then + begin get_gf_file; + if font_state[cur_font]=0 then + begin + if in_gf(font_scaled_size[cur_font]) then + font_state[cur_font]:=1 else font_state[cur_font]:=-1; + end; + if font_state[cur_font]=2 then + begin + if in_tfm(font_scaled_size[cur_font]) then + font_state[cur_font]:=2 else font_state[cur_font]:=-1; + end; + end; +im_byte(set_family); im_byte(cur_font); +goto done + +@* Using the backpointers. +The routines in this section of the program are brought into play only +if |random_reading| is |true|. +First comes a routine that illustrates how to find the postamble quickly. + +@<Find the postamble, working back from the end@>= +n:=dvi_length; +if n<53 then bad_dvi('only ',n:1,' bytes long'); +@.only n bytes long@> +m:=n-4; +repeat if m=0 then bad_dvi('all 223s'); +@.all 223s@> +move_to_byte(m); k:=get_byte; decr(m); +until k<>223; +if k<>id_byte then bad_dvi('ID byte is ',k:1); +@.ID byte is wrong@> +move_to_byte(m-3); q:=signed_quad; +if (q<0)or(q>m-33) then bad_dvi('post pointer ',q:1,' at byte ',m-3:1); +@.post pointer is wrong@> +move_to_byte(q); k:=get_byte; +if k<>post then bad_dvi('byte ',q:1,' is not post'); +@.byte n is not post@> +post_loc:=q; first_backpointer:=signed_quad; + +@ Note that the last steps of the above code save the locations of the +the |post| byte and the final |bop|. We had better declare these global +variables, together with others that we will need shortly. + +@<Glob...@>= +@!post_loc:integer; {byte location where the postamble begins} +@!first_backpointer:integer; {the pointer following |post|} +@!new_backpointer:integer; {the current |bop| command location} + +@ The following routine locates the postamble in order to read the value +of the |first_backpointer| but then processes the pages starting with the +last page so that the pages will be stacked properly by the \.{IMAGEN}. + +@<Find the postamble then process the pages in reverse order@>= +q:=post_loc; +move_to_byte(q); k:=get_byte; +if k<>post then bad_dvi('byte ',q:1,' is not post'); +@.byte n is not post@> +first_backpointer:=signed_quad; +new_backpointer:=first_backpointer; +while (new_backpointer<>-1) and (counter<f_count) do next_page; +while im_byte_no mod 4 <> 3 do im_byte(im_no_op); +im_byte(im_eof); + +@* Reading the postamble. +Now imagine that we are reading the \.{DVI} file and positioned just +four bytes after the |post| command. That, in fact, is the situation, +when the following part of \.{DVIIMP} is called upon to read, translate, +and check the rest of the postamble. + +@p procedure read_postamble; +var k:integer; {loop index} +@!p:integer; {general purpose registers} +begin +post_loc:=cur_loc-5; +if signed_quad<>numerator then + print_ln('numerator doesn''t match the preamble!'); +@.numerator doesn't match@> +if signed_quad<>denominator then + print_ln('denominator doesn''t match the preamble!'); +@.denominator doesn't match@> +if signed_quad<>mag then if new_mag=0 then + print_ln('magnification doesn''t match the preamble!'); +@.magnification doesn't match@> +max_v:=signed_quad; max_h:=signed_quad;@/ +max_s:=get_two_bytes; total_pages:=get_two_bytes;@/ +@<Process the font definitions of the postamble@>; +end; + +@ @<Process the font definitions...@>= +repeat k:=get_byte; +if (k>=fnt_def1)and(k<fnt_def1+4) then + begin p:=first_par(k); + identify_font(p); k:=nop; + end; +until k<>nop; +if k<>post_post then + print_ln('byte ',cur_loc-1:1,' is not postpost!') +@.byte n is not postpost@> + +@ @<Establish range of pages to be printed@>= +if f_flag=false then f_count:=total_pages else + begin f_count:=0; q:=post_loc; + move_to_byte(q); p:=get_byte; + page_match:=false; f_count:=0; + first_backpointer:=signed_quad; + new_backpointer:=first_backpointer; + while (new_backpointer<>-1) and (page_match=false) do back_count; + end; +if n_flag=false then l_count:=1 else l_count:=f_count-num_pages+1; + +@* The main program. +Now we are ready to put it all together. This is where \.{DVIIMP} starts, +and where it ends. + +@p begin initialize; {get all variables initialized} +@<Process the preamble@>; +open_im_file; +@<Find the postamble, working back from the end@>; +read_postamble; +@<Establish range of pages to be printed@>; +@<Find the postamble then process the pages in reverse order@>; +final_end:end. + +@ The main program needs a few global variables in order to do its work. + +@<Glob...@>= +@!k,@!m,@!n,@!p,@!q:integer; {general purpose registers} +@!id_len: 0..255; +@!id: packed array[0..255] of 0..255; + + +@ A \.{DVI}-reading program that reads the postamble first need not look at the +preamble; but \.{DVIIMP} looks at the preamble in order to do error +checking, and to display the introductory comment. + +@<Process the preamble@>= +open_dvi_file; +p:=get_byte; {fetch the first byte} +if p<>pre then bad_dvi('First byte isn''t start of preamble!'); +@.First byte isn't...@> +p:=get_byte; {fetch the identification byte} +if p<>id_byte then + bad_dvi('identification in byte 1 should be ',id_byte:1,'!'); +@.identification...should be n@> +@<Compute the conversion factor@>; +id_len:=get_byte; {fetch the length of the introductory comment} +p:=0; +while p<id_len do + begin incr(p); id[p]:=get_byte; + end; + +@ The conversion factor |conv| is figured as follows: There are exactly +|n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch, +and |resolution| pixels per inch. Then we have to adjust this +by the stated amount of magnification. + +@<Compute the conversion factor@>= +numerator:=signed_quad; denominator:=signed_quad; +if numerator<=0 then bad_dvi('numerator is ',numerator:1); +@.numerator is wrong@> +if denominator<=0 then bad_dvi('denominator is ',denominator:1); +@.denominator is wrong@> +conv:=(numerator/254000.0)*(resolution/denominator); +mag:=signed_quad; +if new_mag>0 then mag:=new_mag +else if mag<=0 then bad_dvi('magnification is ',mag:1); +@.magnification is wrong@> +true_conv:=conv; conv:=true_conv*(mag/1000.0); + +@* System-dependent changes. +This section should be replaced, if necessary, by changes to the program +that are necessary to make \.{DVIIMP} work at a particular installation. +It is usually best to design your change file so that all changes to +previous sections preserve the section numbering; then everybody's version +will be consistent with the printed program. More extensive changes, +which introduce new sections, can be inserted here; then only the index +itself will get a new section number. +@^system dependencies@> + +@* Index. +Pointers to error messages appear here together with the section numbers +where each ident\-i\-fier is used. |