From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- dviware/laserjet/dviplus.web | 2655 ++++++++++++++++++++++++++++++++++++++++++ dviware/laserjet/readme | 22 + 2 files changed, 2677 insertions(+) create mode 100644 dviware/laserjet/dviplus.web create mode 100644 dviware/laserjet/readme (limited to 'dviware/laserjet') diff --git a/dviware/laserjet/dviplus.web b/dviware/laserjet/dviplus.web new file mode 100644 index 0000000000..a47f87a932 --- /dev/null +++ b/dviware/laserjet/dviplus.web @@ -0,0 +1,2655 @@ +% DVIPLUS.WEB +% +% This program is not copyrighted and may be used freely. +% +% Written by Tor Lillqvist +% Technical Research Centre of Finland +% Lehtisaarentie 2 A +% SF-00340 HELSINKI +% FINLAND +% +% E-mail: tml@@fingate.bitnet, tml@@santra.uucp, ...!mcvax!santra!tml +% Phone: +358 0 4566132 +% Telex: 122972 vttha sf + +% 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 SAIL +\def\PASCAL{Pascal} + +\def\(#1){} % this is used to make section names sort themselves better +\def\9#1{} % this is used for sort keys in the index + +\def\title{DVI$\,$\lowercase{plus}} +\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 DVIplus} processor} + \vskip 10pt + \centerline{\titlefont for HP LaserJet+} + \vskip 15pt + \centerline{(Version 1.2, April 1986)} + \vfill} +\def\botofcontents{\vfill + \centerline{\baselineskip9pt + \vbox{\ninerm\noindent + `\TeX' is a + trademark of the American Mathematical Society.}}} +\pageno=\contentspagenumber \advance\pageno by 1 + +@* Introduction. +The \.{DVIplus} program reads binary device-independent (``\.{DVI}'') +files that are produced by document compilers such as \TeX, +and translates them for printing on a HP LaserJet+ +page printer. +This program is written by Tor Lillqvist, +based on the DVItype program by Donald E.~Knuth. + +This version is for the \PASCAL/1000 compiler on the RTE--A operating +system running on the HP1000 A--Series computers. + +Programs for +typesetting need to be especially careful about how they do arithmetic; if +rounding errors accumulate, margins won't be straight, vertical rules +won't line up, and so on. But if rounding is done everywhere, even in the +midst of words, there will be uneven spacing between the letters, and that +looks bad. Human eyes notice differences of a thousandth of an inch in the +positioning of lines that are close together; on low resolution devices, +where rounding produces effects four times as great as this, the problem +is especially critical. Experience has shown that unusual care is needed +even on high-resolution equipment; for example, a mistake in the sixth +significant hexadecimal place of a constant once led to a difficult-to-find +bug in some software for the Alphatype CRS, which has a resolution of 5333 +pixels per inch (make that 5333.33333333 pixels per inch). The document +compilers that generate \.{DVI} files make certain assumptions about the +arithmetic that will be used by \.{DVI}-reading software, and if these +assumptions are violated the results will be of inferior quality. +Therefore the present program is intended as a guide to proper procedure +in the critical places where a bit of subtlety is involved. + +The |banner| string defined here should be changed whenever \.{DVIplus} +gets modified. +The editor of RTE--A automagically updates this timestamp when +the file is written. + +@d banner=='This is DVIplus, RTE-A Version 1.2 <860605.2059>' + +@ This program is not written in standard \PASCAL, but +the \PASCAL/1000 dialect used on HP1000 A--series computers +running the RTE--A operating system; +it should be easy to convert to other reasonable \PASCAL\ +dialects. + +Places where pecliarities depndent on the RTE--A implmentation have +been used are 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. +In fact, in RTE--A we cannot determine the logical length of a binary file, +so another approach is used: The first doubleword in \.{PXL} and \.{DVI} +files contains the size of the file (in doublewords = integers). +The records are numbered sequentially starting from 1. The internal pointers +in \.{DVI} and \.{PXL} files start the (byte) numbering from 0, so an offset +is added in the |seek| calls. + +\PASCAL/1000 allows writing nonprinting characters to |text| files +using a notation with a number sign followed by the decimal +ordinal number of the character. For example, |write(f, #27'E')| writes +an escape followed by a capital `E'. + +In \PASCAL/1000, output to |text| files is line buffered, and we cannot +keep |write|ing without using |writeln| every now and then. +This terminates the record, which +normally causes a newline, but the terminal drivers in RTE--A +leave the newline out if the last byte of a write request is underscore. +This is handled by the |write_lj| macro. + +Another extension is to use a default |case| as in \.{TANGLE}, \.{WEAVE}, +etc. + +@d othercases == otherwise {default for cases not listed explicitly} +@d endcases == @+end {follows the default case in an extended |case| statement} +@f othercases == else +@f endcases == end + +@ Before the program heading we have some compiler options. +These specify that code should be generated for the `CDS' mode, +i.e. Code-and-Data-Separation. + +The binary input comes from |dvi_file| and the |pxl_file|s. +@^system dependencies@> + +@p +@=$Standard_Level 'HP1000', CDS On, Debug, Range Off$@>@/ +program DVIplus; +type @@/ +var @@/ +@@/ +procedure initialize; {this procedure gets things started properly} +var i:0..255; +begin + @@/ +end; + +@ Here are some macros for common programming idioms. + +@d do_nothing == {empty statement} +@d incr(#) == #:=#+1 {increase a variable by unity} +@d decr(#) == #:=#-1 {decrease a variable by unity} + +@ We do our own error handling. +@^system dependencies@> + +@d catch_errors==error_status:=true +@d dont_catch_errors==error_status:=false + +@= +@!error_type=(err_run,err_ema,err_io,err_fmp,err_seg,err_wrn); +@!error_file_name=packed array [1..150] of char; + +@ The |error_status| flag is set |true| when errors are to be catched. +If a file system related error occurs, |error_status| is set false. + +@= +@!error_status:boolean; + +@ If an error that we are not prepared to catch occurs, we call +the standard error printing routine, and |halt|. + +@p procedure errorprinter @=$Alias 'Pas.ErrorPrinter'$@> +(err_type: error_type; err_number, err_line: short; +var err_file: error_file_name; err_flen: short); + external;@t\2@>@/ +@# +procedure errorcatcher @=$Alias 'Pas.ErrorCatcher'$@> +(err_type: error_type; err_number, err_line: short; +var err_file: error_file_name; err_flen: short); +begin + if err_type=err_wrn then + do_nothing + else if error_status and (err_type=err_fmp) then + error_status := false + else begin + errorprinter(err_type,err_number,err_line,err_file,err_flen); + halt(1); {an error we weren't prepared to catch} + end; +end; + +@ External routines are declared here. +|parameters| retrieves a runstring parameter. +|LURQ| is used to lock the output device. +|dcb_address| returns a pointer to the DCB (a sort of file control +block for an open file. +|fmp_interactive| tells if a DCB corresponds to an interactive device, +and |fmp_lu| returns the `logical unit' number of a device. +@^system dependencies@> + +@= +function parameters @=$Alias 'Pas.Parameters'$@> + (pos:short; var par:file_name; len:short):short; external; @t\2@>@# + +procedure LURQ(option:short; var lu:short; num: short; var key:short); + external; @t\2@>@# + +procedure dcb_address @=$Alias 'Pas.DcbAddress1'$@> + (var p:dcb_ptr; var f:text_file); external; @t\2@>@# + +function fmp_interactive @=$Alias 'FmpInteractive'$@> + (var d:dcb):short; external; @t\2@>@# + +function fmp_lu @=$Alias 'FmpLU'$@> (var d:dcb):short; external; @t\2@>@# + +@ Types related to system routines. + +@d name_length=64 {a file name shouldn't be longer than this} + +@= +@!short=-32768..32767; +@!file_name=packed array [1..name_length] of char; +@!dcb=array [1..144] of short; +@!dcb_ptr=^dcb; + +@ @= +@!laser_dcb:dcb_ptr; +@!lu,@!key:short; + +@ We lock the printer after opening it if it is a device. +This prevents intermixed output. +The default output file name is given here. +@^system dependencies@> + +@d default_out=='61' + +@= +if parameters(arg_index,cur_name,name_length) <= 0 then + cur_name:=default_out; +rewrite(laser_file,cur_name,'NOCCTL,SHARED'); +dcb_address(laser_dcb,laser_file); +if fmp_interactive(laser_dcb^) <> 0 then begin + lu:=fmp_lu(laser_dcb^); + LURQ(1,lu,1,key); +end + +@ Labels (global and local). + +@d done=30 {go here when finished with a subtask} +@d exit=999 {go here to leave a procedure} +@d return==goto exit + +@ The following parameters can be changed to extend or +reduce \.{DVIplus}'s capacity. + +@d max_fonts=75 {maximum number of distinct fonts per \.{DVI} file} +@d max_printer_fonts=20 {maximum number of fonts kept in printer} +@d max_fonts_on_page=16 {maximum number of fonts per page} +@d max_widths=9601 {|max_fonts * 128 + 1|} + {maximum number of different characters among all fonts} +@d line_length=79 {bracketed lines of output will be at most this long} +@d terminal_line_length=128 {maximum number of characters input in a single + line of input from the terminal} +@d stack_size=100 {\.{DVI} files shouldn't |push| beyond this depth} +@d name_size=1000 {total length of all font file names} +@d max_bops=1000 {maximum number of pages printed} + +@ If the \.{DVI} file is badly malformed, the whole process must be aborted; +\.{DVIplus} 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 simply +halts the program using the \PASCAL/1000 procedure |halt|. +@^system dependencies@> + +@d print(#)==write(term_out,#) +@d print_ln(#)==write_ln(term_out,#) +@d print_nl==write_ln(term_out) +@d abort(#)==begin print('? ',#); jump_out; + end +@d bad_dvi(#)==abort('Bad DVI file: ',#,'!') +@.Bad DVI file@> +@d bad_pxl(#)==begin print_nl; abort('Bad PXL file: ',#,'!'); end +@.Bad PXL file@> + +@p procedure jump_out; +begin halt(1) +end; + +@ We fill output lines to |laser_file|, breaking the line when +|lj_threshold| characters have been written. An underscore character +is appended to the line before the newline (record boundary). +This prevents the RTE--A terminal driver from writing a {\tt CR~LF} +pair at the end of the line. +@^system dependencies@> + +All output to |laser_file| goes through the macros |write_lj| or +|write_lj_f|. The latter doesn't test for line break before writing. + +@d lj_threshold=70 + +@d write_lj(#)==begin if linepos(laser_file)>lj_threshold then + write_ln(laser_file,'_'); + write(laser_file,#) +end +@d write_lj_f(#)==write(laser_file,#) + +@* The character set. +Like all programs written with the \.{WEB} system, \.{DVIplus} 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 \.{DVIplus} have therefore been copied from the +analogous ones in the \.{WEB} system routines. They have been considerably +simplified, since \.{DVIplus} 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. + +@= +@!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 \.{DVIplus}. +So we shall assume that the \PASCAL\ system being used for \.{DVIplus} +has a character set containing at least the standard visible characters +of ASCII code (|"!"| through |"~"|). + +Some \PASCAL\ compilers use the original name |char| for the data type +associated with the characters in text files, while other \PASCAL s +consider |char| to be a 64-element subrange of a larger data type that has +some other name. In order to accommodate this difference, we shall use +the name |text_char| to stand for the data type of the characters in the +output file. We shall also assume that |text_char| consists of +the elements |chr(first_text_char)| through |chr(last_text_char)|, +inclusive. The following definitions should be adjusted if necessary. +@^system dependencies@> + +@d text_char == char {the data type of characters in text files} +@d first_text_char=0 {ordinal number of the smallest element of |text_char|} +@d last_text_char=255 {ordinal number of the largest element of |text_char|} + +@= +@!text_file=text_; + +@ The \.{DVIplus} 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. + +@= +@!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. + +@= +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|. + +@= +for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40; +for i:=" " to "~" do xord[xchr[i]]:=i; + +@ The LaserJet refuses to print other that `visible characters', that is +characters in the range @'41--@'177 or @'240--@'377. + +@p function vis_chr(p : integer):char; +begin + if p < @'41 then + p:=p+@'240; + vis_chr:=chr(p); +end; + +@* Device-independent file format. +Before we get into the details of \.{DVIplus}, 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. + +\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 \.{DVIplus} 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. \.{DVIplus} 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 dvi_id=2 {identifies the kind of \.{DVI} files described here} + +@ Font definitions for a given font number |k| contain further parameters +$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$ +The four-byte value |c| is the check sum that \TeX\ (or whatever program +generated the \.{DVI} file) found in the \.{TFM} file for this font; +|c| should match the check sum of the font found by programs that read +this \.{DVI} file. +@^check sum@> + +Parameter |s| contains a fixed-point scale factor that is applied to the +character widths in font |k|; font dimensions in \.{TFM} files and other +font files are relative to this quantity, which is always positive and +less than $2^{27}$. It is given in the same units as the other dimensions +of the \.{DVI} file. Parameter |d| is similar to |s|; it is the ``design +size,'' and it is given in \.{DVI} units that have not been corrected for +the magnification~|mag| found in the preamble. Thus, font |k| is to be +used at $|mag|\cdot s/1000d$ times its normal size. + +The remaining part of a font definition gives the external name of the font, +which is an ASCII string of length |a+l|. The number |a| is the length +of the ``area'' or directory, and |l| is the length of the font name itself; +the standard local system font area is supposed to be used when |a=0|. +The |n| field contains the area in its first |a| bytes. + +Font definitions must appear before the first use of a particular font number. +Once font |k| is defined, it must not be defined again; however, we +shall see below that font definitions appear in the postamble as well as +in the pages, so in this sense each font number is defined exactly twice, +if at all. Like |nop| commands and \\{xxx} commands, font definitions can +appear before the first |bop|, or between an |eop| and a |bop|. + +@ The last page in a \.{DVI} file is followed by `|post|'; this command +introduces the postamble, which summarizes important facts that \TeX\ has +accumulated about the file, making it possible to print subsets of the data +with reasonable efficiency. The postamble has the form +$$\vbox{\halign{\hbox{#\hfil}\cr + |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr + $\langle\,$font definitions$\,\rangle$\cr + |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$ +Here |p| is a pointer to the final |bop| in the file. The next three +parameters, |num|, |den|, and |mag|, are duplicates of the quantities that +appeared in the preamble. + +Parameters |l| and |u| give respectively the height-plus-depth of the tallest +page and the width of the widest page, in the same units as other dimensions +of the file. These numbers might be used by a \.{DVI}-reading program to +position individual ``pages'' on large sheets of film or paper. + +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. + +@* Input from binary files. +We have seen that a \.{DVI} file is a sequence of 8-bit bytes. The bytes +appear physically in what is called a `|packed file of 0..255|' +in \PASCAL\ lingo. + +Packing is system dependent, and many \PASCAL\ systems fail to implement +such files in a sensible way (at least, from the viewpoint of producing +good production software). For example, some systems treat all +byte-oriented files as text, looking for end-of-line marks and such +things. Therefore some system-dependent code is often needed to deal with +binary files. +@^system dependencies@> + +In the RTE--A implementation, \.{DVI} and \.{PXL} files are files of +|integer|s, +with the first |integer| containing the number of |integer|s to follow. +(It is impossible to know the logical size of a random-access file). +The bytes of the file are accessed through a variant record. + +@d pxl_id=1001 {identifies the kind of \.{PXL} files handled} + +@= +@!eight_bits=0..255; {unsigned one-byte quantity} +four=0..3; +@!i2c=packed record case four of 0: (i:integer);@/ +1:(i0,i1:short);@/ +2:(b:packed array[0..3] of eight_bits); +3:(c:packed array[0..3] of char) end; +@!int_file=file of integer; +@!byte_file=record {files that contain binary data} +x:i2c;i:0..3;m,s:integer; +f:int_file; +end; + +@ The program deals with the binary file variables |dvi_file| which is the +main input file that we are printing, and |pxl_file| is an array of +font image files from which character images is downloaded +to the printer. + +@= +@!laser_file:text_file; +@!dvi_file:byte_file; {the stuff we are \.{DVI}typing} +@!pxl_file:array[0..max_fonts] of file of i2c; {font image files} +@!s_pxl_file:integer; {size of |pxl_file|} + +@ To prepare these files for input, we |reset| them. An extension of +\PASCAL\ is needed in the case of |pxl_file|, since we want to associate +it with external files whose names are specified dynamically (i.e., not +known at compile time). The following code assumes that `|reset(f,s)|' +does this, when |f| is a file variable and |s| is a string variable that +specifies the file name. +The function result is false if the file couldn't be opened. +@^system dependencies@> + +@p function open_dvi_file:boolean; +var i,j:short; +begin +i:=parameters(arg_index,cur_name,name_length); +incr(arg_index); +j:=1; +while (j<=i)and(cur_name[j]<>'.') do incr(j); +if j>i then begin + cur_name[j]:='.'; incr(j); + cur_name[j]:='d'; incr(j); + cur_name[j]:='v'; incr(j); + cur_name[j]:='i'; incr(j); +end; +catch_errors; +reset(dvi_file.f,cur_name,'SHARED'); +open_dvi_file:=error_status; +if error_status then begin + dont_catch_errors; + close(dvi_file.f); + open(dvi_file.f,cur_name,'SHARED'); + get(dvi_file.f); + dvi_file.s:=dvi_file.f^; + get(dvi_file.f); + dvi_file.x.i:=dvi_file.f^; + dvi_file.i:=0; +end; +cur_loc:=0; +end; +@# +function open_pxl_file(i:short):boolean; +begin +catch_errors; +reset(pxl_file[i],cur_name,'SHARED'); +open_pxl_file:=error_status; +if error_status then begin + dont_catch_errors; + close(pxl_file[i]); + open(pxl_file[i],cur_name,'SHARED'); + get(pxl_file[i]); + s_pxl_file:=pxl_file[i]^.i; + get(pxl_file[i]); + if pxl_file[i]^.i <> pxl_id then + bad_pxl('bad header id'); +end; +end; + +@ If you looked carefully at the preceding code, you probably asked, +``What are |cur_loc| and |cur_name|?'' Good question. They're global +variables: |cur_loc| is the number of the byte about to be read next from +|dvi_file|, and |cur_name| is a string variable that will be set to the +current pixel image file name before |open_pxl_file| is called. + +@= +@!cur_loc:integer; {where we are about to look, in |dvi_file|} +@!cur_name:file_name; {external name, + with no lower case letters} + +@ It turns out to be convenient to read four bytes at a time, when we are +inputting from \.{PXL} files. The input goes into global variables +|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3| +the fourth. + +@= +@!b0,@!b1,@!b2,@!b3: integer; {four bytes input at once} + +@ The |read_pxl_word| procedure sets |b0| through |b3| to the next +four bytes in the current \.{TFM} file. +@^system dependencies@> + +@p procedure read_pxl_word(i:short); +begin +b0:=pxl_file[i]^.b[0];b1:=pxl_file[i]^.b[1]; +b2:=pxl_file[i]^.b[2];b3:=pxl_file[i]^.b[3]; +get(pxl_file[i]); +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@> + +This is the best way to check ``eof'' in RTE--A, as the logical length of +the file is unknown (except from the first word). + +@d eof_dvi_file==(cur_loc>dvi_file.s*4) + +@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 + get_byte:=dvi_file.x.b[dvi_file.i]; + if dvi_file.i=3 then begin + get(dvi_file.f); + dvi_file.x.i:=dvi_file.f^; + dvi_file.i:=0; end + else + incr(dvi_file.i); + end; + incr(cur_loc); +end; +@# +function signed_byte:integer; {returns the next byte, signed} +var b:eight_bits; +begin b:=get_byte; +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:integer; +begin a:=get_byte; b:=get_byte; +get_two_bytes:=a*256+b; +end; +@# +function signed_pair:integer; {returns the next two bytes, signed} +var a,@!b:integer; +begin a:=get_byte; b:=get_byte; +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 a:=get_byte; b:=get_byte; c:=get_byte; +get_three_bytes:=(a*256+b)*256+c; +end; +@# +function signed_trio:integer; {returns the next three bytes, signed} +var a,@!b,@!c:integer; +begin a:=get_byte; b:=get_byte; c:=get_byte; +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:integer; +begin a:=get_byte; b:=get_byte; c:=get_byte; d:=get_byte; +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 to acces the |dvi_file| +randomly. 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. + +@d dvi_length==(dvi_file.s*4) +@# +@d move_to_byte(#)==begin seek(dvi_file.f,((#) div 4)+2); get(dvi_file.f); +dvi_file.x.i:=dvi_file.f^; dvi_file.i:=(#) mod 4; cur_loc:=#; end + +@ +Font files +should contain exactly the same character width data that is +found in the corresponding \.{TFM}s; check sums are used to help +ensure this. In addition, font files also contain the widths of +characters in pixels, since the device-independent character widths of +\.{TFM} files are generally not perfect multiples of pixels. + +The |pixel_width| array contains this information; when |width[k]| is the +device-independent width of some character in \.{DVI} units, |pixel_width[k]| +is the corresponding width of that character in an actual font. +The macro |char_pixel_width| is set up to be analogous to |char_width|. + +The |status| array tells if a charater has been downloaded to the +laser printer, or if it is too large, and must be transferred as raster +graphics. It might also contain a positive value that indicates a shift +up of the reference point (because the ``baseline'' of the characters +is set at |baseline| pixels from the bottom, characters can't be deeper than +|baseline| pixels). + +The large arrays are located in +EMA (Extended Memory Area) (outside the 32 page range addressable +with one-word addresses). +The \.{Ema\_Var} compiler options specify which variables are in EMA. +@^system dependencies@> + +@d char_end_width(#)==#] +@d char_pixel_width(#)==pixel_width[width_base[#]+char_end_width +@d char_status(#)==status[width_base[#]+char_end_width +@d not_loaded=0 +@d too_large=-1 +@d loaded_ok=-2 +@d baseline=55 + +@= +@=$Ema_Var On$@> +@!pixel_width:array[0..max_widths] of short; {actual character widths, + in pixels} +@!status:array[0..max_widths] of short; {character statuses} +@=$Ema_Var Off$@> +@!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} +@!desired_mag:integer; + +@* Reading the font information. +The current number of known fonts is |nf|. Each known font has +an internal number |f|, where |0<=finvalid_width|. +Finally, |char_width(f)(c)=width[width_base[f]+c]|, and |width_ptr| is the +first unused position of the |width| array. + +@d char_width(#)==width[width_base[#]+char_end_width +@d invalid_width==@'17777777777 + +@= +@!font_num:array [0..max_fonts] of integer; {external font numbers} +@!font_name:array [0..max_fonts] of 0..name_size; {starting positions + of external font names} +@!names:array [0..name_size] of ASCII_code; {characters of names} +@!font_check_sum:array [0..max_fonts] of integer; {check sums} +@!font_scaled_size:array [0..max_fonts] of integer; {scale factors} +@!font_design_size:array [0..max_fonts] of integer; {design sizes} +@!font_mag:array [0..max_fonts] of integer; +@!font_space:array [0..max_fonts] of integer; {boundary between ``small'' + and ``large'' spaces} +@!font_used_on:array[0..max_fonts] of integer; {on which page last used} +@!width_base:array [0..max_fonts] of integer; {index into |width| table} +@=$Ema_Var On$@> +@!width:array [0..max_widths] of integer; {character widths, in \.{DVI} units} +@=$Ema_Var Off$@> +@!nf:0..max_fonts; {the number of known fonts} +@!width_ptr:0..max_widths; {the number of known character widths} + +@ @= +nf:=0; width_ptr:=0; font_name[0]:=0; font_space[0]:=0; +font_used_on[0]:=0; + +@ It is, of course, a simple matter to print the name of a given font. + +@p procedure print_font(@!f:integer); {|f| is an internal font number} +var k:0..name_size; {index into |names|} +begin if f=nf then print('UNDEFINED!') +@.UNDEFINED@> +else begin for k:=font_name[f] to font_name[f+1]-1 do + print(xchr[names[k]]); + end; +end; + +@ The global variabls |pxl_check_sum|, +|pxl_design_size| are set from the \.{PXL} file. + +@= +@!pxl_check_sum:integer; {check sum found in |pxl_file|} +@!pxl_dptr:array [0..max_fonts] of integer; {directory pointers} +@!pxl_design_size:integer; + +@ Here is a procedure that absorbs the necessary information from a +\.{PXL} file, assuming that the file has just been successfully opened. +(A complete description of +\.{PXL} file format appears elsewhere and will +not be repeated here.) The procedure does not check the \.{PXL} file +for validity, nor does it give explicit information about what is +wrong with a \.{PXL} file that proves to be invalid; \.{DVI}-reading +programs need not do this, since \.{PXL} files are almost always valid. +The procedure simply returns |false| if it +detects anything amiss in the \.{PXL} data. + +There is a parameter, |z|, which represents the scaling factor being +used to compute the font dimensions; it must be in the range $0max_widths then begin + print_nl; + abort('Need larger width table'); + end; + width_base[nf]:=width_ptr; + wp:=width_ptr; + @; + @; + @; + width_ptr:=wp; +end; + +@ @= +begin + seek_pxl(nf)(0); + get(pxl_file[nf]); + if pxl_file[nf]^.i <> pxl_id then + bad_pxl('bad header id'); +end + +@ @= +begin + seek(pxl_file[nf],s_pxl_file+1); + get(pxl_file[nf]); + if pxl_file[nf]^.i <> pxl_id then + bad_pxl('bad trailer id'); + seek(pxl_file[nf],s_pxl_file-3); + get(pxl_file[nf]); + read_pxl(pxl_check_sum); + read_pxl(font_mag[nf]); + read_pxl(pxl_design_size); + read_pxl(pxl_dptr[nf]); +end; + +@ One important part of |in_PXL| is the width computation, which +involves multiplying the relative widths in the \.{PXL} 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 \.{PXL} +file appears as a four-byte quantity called a |fix_word|. A |fix_word| +whose respective bytes are $(a,b,c,d)$ represents the number +$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr +b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr +-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$ +(No other choices of $a$ are allowed, since the magnitude of a \.{TFM} +dimension must be less than 16.) We want to multiply this quantity by the +integer~|z|, which is known to be less than $2^{27}$. Let $\alpha=16z$. +If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$, +$d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or +16, to obtain a multiplier less than $2^{23}$, and we can compensate for +this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let +$\beta=2^{4-e}$; we shall compute +$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$, +or the same quantity minus $\alpha$ if $a=255$. This calculation must be +done exactly, for the reasons stated above; the following program does the +job in a system-independent way, assuming that arithmetic is exact on +numbers less than $2^{31}$ in magnitude. + +The following code computes pixel widths by simply rounding the \.{PXL} +widths to the nearest integer number of pixels, based on the conversion factor +|conv| that converts \.{DVI} units to pixels. However, such a simple +formula will not be valid for all fonts, and it will often give results that +are off by $\pm1$ when a low-resolution font has been carefully +hand-fitted. For example, a font designer often wants to make the letter `m' +a pixel wider or narrower in order to make the font appear more consistent. +\.{DVI}-to-printer programs should therefore input the correct pixel width +information from font files whenever there is a chance that it may differ. +A warning message may also be desirable in the case that at least one character +is found whose pixel width differs from |conv*width| by more than a full pixel. + +Those characters that are too large to be downloadable, are marked as such, +and will be transferred using raster graphics. We must be especially careful +in the y dimension, as the character must fit into the 255*255 box when +the reference point is placed on the baseline. The baseline was set to +$255-$|baseline| pixel rows down from the top. +@^system dependencies@> + +@d pixel_round(#)==round(conv*(#)) + +@= +@; +for k:=0 to 127 do begin + seek(pxl_file[nf],pxl_dptr[nf]+k*4+2); + get(pxl_file[nf]); + {is the charater too large ?} + if (pxl_file[nf]^.i0 >= 128) or (pxl_file[nf]^.i1 >= 128) then + status[wp]:=too_large + else + status[wp]:=not_loaded; + get(pxl_file[nf]); + get(pxl_file[nf]); + if pxl_file[nf]^.i=0 then begin + width[wp]:=invalid_width; + pixel_width[wp]:=0 end + else begin + get(pxl_file[nf]); + read_pxl_word(nf); + width[wp]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta; + if b0>0 then if b0<255 then bad_pxl('strange width for char ', k:1) + else width[wp]:=width[wp]-alpha; + pixel_width[wp]:=pixel_round(width[wp]); + end; + incr(wp); +end + +@ @= +begin alpha:=16*z; beta:=16; +while z>=@'40000000 do + begin z:=z div 2; beta:=beta div 2; + end; +end + +@* Downloading information to the printer. +The procedure |update_pos| is used to update the printer cursor position. +This is the only place where the cursor is explicitely positioned. + +@p procedure update_pos; +label done; +begin if (lj_h<>hh)or(lj_v<>vv) then begin + write_lj(@=#27'*p'@>); + if (lj_h<>hh) then begin + if abs(lj_h-hh) > hh div 10 then + write_lj_f(hh:1) + else if (lj_hvv) then + write_lj_f('x') + else begin + write_lj_f('X'); + goto done; + end + end; + if abs(lj_v-vv) > vv div 10 then + write_lj_f(vv:1) + else if (lj_v + +@p procedure download_char(p:integer); +var i,j:short; +@!pixel_wd,@!pixel_ht:integer; +@!offsets,char_wd,char_ht,delta_x:i2c; +@!raster_address:integer; + +begin + seek(pxl_file[cur_font],pxl_dptr[cur_font]+p*4+2); + get(pxl_file[cur_font]); + pixel_wd:=pxl_file[cur_font]^.i0; + pixel_ht:=pxl_file[cur_font]^.i1; + get(pxl_file[cur_font]); + offsets:=pxl_file[cur_font]^; + get(pxl_file[cur_font]); + raster_address:=pxl_file[cur_font]^.i; + @; + seek(pxl_file[cur_font],raster_address+2); + for j:=1 to pixel_ht do begin + for i:=1 to (pixel_wd-1) div 8 + 1 do begin + if (i-1) mod 4 = 0 then + get(pxl_file[cur_font]); + write_lj(pxl_file[cur_font]^.c[(i-1) mod 4]); + end; + end +end; + +@ Download a character header. + +@d data_bytes==(pixel_ht*(((pixel_wd-1) div 8)+1)) + +@= +begin + if pixel_ht - offsets.i1 > baseline then begin + char_status(cur_font)(p):=pixel_ht-offsets.i1; + offsets.i1:=pixel_ht; end + else + char_status(cur_font)(p):=loaded_ok; + offsets.i0:=-offsets.i0; + char_wd.i0:=pixel_wd; + char_ht.i0:=pixel_ht; + delta_x.i0:=char_pixel_width(cur_font)(p)*4; + write_lj(@=#27'*c'@>,font_num[cur_font]:1,'d', + ord(vis_chr(p)):1,@='E'#27'(s'@>, + data_bytes+16:1,'W'+ + @=#4#0#14#1#0#0@>, + offsets.c[0], offsets.c[1], + offsets.c[2], offsets.c[3], + char_wd.c[0], char_wd.c[1], + char_ht.c[0], char_ht.c[1], + delta_x.c[0], delta_x.c[1]); +end + +@ This procedure transfers a character in raster form. +This is used for characters which are too large to be stored in +the font memory of the printer. +@^system dependencies@> + +@p procedure raster_char(p:integer); +var +i,j,n:short; +@!pixel_ht,@!pixel_wd,@!x_offset,@!y_offset:short; +@!raster_address:integer; + +begin + seek(pxl_file[cur_font],pxl_dptr[cur_font]+p*4+2); + get(pxl_file[cur_font]); + pixel_wd:=pxl_file[cur_font]^.i0; + pixel_ht:=pxl_file[cur_font]^.i1; + get(pxl_file[cur_font]); + x_offset:=pxl_file[cur_font]^.i0; + y_offset:=pxl_file[cur_font]^.i1; + get(pxl_file[cur_font]); + raster_address:=pxl_file[cur_font]^.i; + hh:=hh-x_offset; vv:=vv-y_offset; + update_pos; + write_lj(@=#27'*r1A'@>); + seek(pxl_file[cur_font],raster_address+2); + for j:=1 to pixel_ht do begin + write_lj(#27'*b',(pixel_wd-1) div 8 + 1:1, 'W'); + for i:=1 to (pixel_wd-1) div 8 + 1 do begin + if (i-1) mod 4 = 0 then + get(pxl_file[cur_font]); + write_lj(pxl_file[cur_font]^.c[(i-1) mod 4]); + end; + end; + lj_v:=lj_v+pixel_ht; + hh:=hh+char_pixel_width(cur_font)(p); vv:=vv+y_offset; + write_lj(@=#27'*rB'@>); +end; + +@* Optional modes of output. +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, +\.{DVIplus} 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. + +Another option is the page offset. That is the point on the physical +printer page where the point (0,0) in the DVI file is mapped. + +Normally \.{DVIplus} uses default values for the options. +It can be started in such a way that it engages +the user in a brief dialog so that the +options will be specified. +@^system dependencies@> + +@= +@!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} +@!copies:integer; +@!h_offset,@!v_offset:integer; {offset where to put (0,0) on physical page} + +@ 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. + +@= +@!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} + +@ @= +max_pages:=100; start_vals:=0; start_there[0]:=false; copies:=1; +resolution:=300.0; new_mag:=0; +h_offset:=210; v_offset:=100; + +@ 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@> + +@= +@!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} +@!arg_index:short; {which command line argument is being processed} +@!interactive:boolean; + +@ 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 call a system-dependent +subroutine |update_terminal| in order to avoid this problem. +@^system dependencies@> + +@d update_terminal == prompt(term_out) {empty the terminal output buffer} + +@ During the dialog, \.{DVIplus} 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; +k:=0; +if eoln(term_in) then read_ln(term_in) +else begin while (k= +@!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 \.{DVIplus} begins. +@^system dependencies@> + +@p procedure dialog; +label 2,3,6,7,8,9,99; +var i,j,k:short; +begin rewrite(term_out, '1', 'NOCCTL'); {prepare the terminal for output} +reset(term_in, '1'); {and for input} +print_ln(banner); +@; +@; +@; +@; +@; +@; +end; + +@ @= +arg_index:=1; interactive:=false; +repeat + i:=parameters(arg_index,cur_name,name_length); + if cur_name[1]='-' then begin + incr(arg_index); + if i=1 then + interactive:=true + else case cur_name[2] of + 'i','I':interactive:=true; + othercases begin end; + endcases; + end; +until cur_name[1]<>'-'; +if not interactive then goto 99; + +@ @= +2: print('Starting page (default=*): '); +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 print('Type, e.g., 1.*.-5 to specify the '); + print_ln('first page with \count0=1, \count2=-5.'); + goto 2; + end; + until start_vals=k + +@ @= +3: print('Maximum number of pages (default=100): '); +input_ln; buf_ptr:=0; +if buffer[0]<>" " then + begin max_pages:=get_integer; + if max_pages<=0 then + begin print_ln('Please type a positive number.'); + goto 3; + end; + end + +@ @= +6: print('Number of copies (default=1): '); +input_ln; buf_ptr:=0; +if buffer[0]<>" " then + if (buffer[0]>="0")and(buffer[0]<="9") then copies:=get_integer + else begin print('Type a positive integer to specify '); + print_ln('the number of copies.'); + goto 6; + end + +@ @= +7: print('Page offset in dots (default=210,100): '); +input_ln; buf_ptr:=0; +if buffer[0]=" " then goto 9; +if ((buffer[0]>="0")and(buffer[0]<="9")) or (buffer[0]="-") then + h_offset:=get_integer +else goto 8; +if (buffer[buf_ptr]=",") then incr(buf_ptr); +if (buffer[buf_ptr]=" ") then incr(buf_ptr); +if ((buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9")) or + (buffer[buf_ptr]="-") then begin + v_offset:=get_integer; + goto 9; end +else goto 8; +8:print('Specify a dot coordinate pair where the (0,0) '); + print_ln('point will be placed.'); + goto 7; +9: + +@ After the dialog is over, we print the options so that the user +can see what \.{DVIplus} thought was specified. + +@= +99: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 +font_num[nf]:=e; f:=0; +while font_num[f]<>e do incr(f); +@; + if f + @ +end; + +@ @= +c:=signed_quad; font_check_sum[nf]:=c;@/ +q:=signed_quad; font_scaled_size[nf]:=q;@/ +d:=signed_quad; font_design_size[nf]:=d;@/ +p:=get_byte; n:=get_byte; +if font_name[nf]+n+p>name_size then + abort('DVIplus capacity exceeded (name size=',name_size:1,')'); +@.DVIplus capacity exceeded...@> +font_name[nf+1]:=font_name[nf]+n+p; +if n+p=0 then abort('Null font name') +@.Null font name@> +else for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=get_byte; +font_used_on[nf+1]:=0; +incr(nf); +if f=nf-1 then begin + print('Font ', e:1, ': '); print_font(nf-1); + update_terminal; +end; +decr(nf) + +@ @= +begin +@; +@; +if not open_pxl_file(nf) then begin + print_nl; + abort('Cannot open PXL file ', cur_name); end +@.Cannot open PXL file@> +else begin if (q<=0)or(q>=@'1000000000) then begin + print_nl; + abort('PXL file not loaded, bad scale (',q:1,')!'); end +@.bad scale@> + else if (d<=0)or(d>=@'1000000000) then begin + print_nl; + abort('PXL file not loaded, bad design size (',d:1,')!'); end +@.bad design size@> + else begin + in_PXL(q); + @; + end +end +end + +@ @= +desired_mag:=round((mag * q * (300.0/200.0))/d+0.5); + +@ @= +begin font_space[nf]:=q div 6; {this is a 3-unit ``thin space''} +if (c<>0)and(pxl_check_sum<>0)and(c<>pxl_check_sum) then + begin print(' ---beware: check sums do not agree!'); +@.beware: check sums do not agree@> +@.check sums do not agree@> + print(' (',c:1,' vs. ',pxl_check_sum:1,')'); + end; +d:=round((100.0*conv*q)/(true_conv*d)); +if d<>100 then + print(' (magnified ',d:1,'%)'); +@.this font is magnified@> +incr(nf); {now the new font is officially present} +font_space[nf]:=0; {for |out_space| and |out_vmove|} +end + +@ If |p=0|, i.e., if no font directory has been specified, \.{DVIplus} +uses the default font directory, which is a +system-dependent place where the standard fonts are kept. + +In RTE--A, the \.{PXL} files are kept in directories called +\.{/TeX/Fonts/MagXXXX}, where \.{XXXX} is the magnification. +The string variable |default_prefix| contains the prefix of these names. +@^system dependencies@> + +@d default_prefix_name=='/TeX/Fonts/Mag' +@d default_prefix_length=14 {change this to the correct length} + +@= +@!default_prefix:packed array[1..default_prefix_length] of char; + +@ @= +default_prefix:=default_prefix_name; + +@ The string |cur_name| is set to the external name of the +\.{PXL} file for the current font and magnification. +@^system dependencies@> + +@= +for k:=1 to name_length do cur_name[k]:=' '; +if p=0 then + begin for k:=1 to default_prefix_length do + cur_name[k]:=default_prefix[k]; + r:=default_prefix_length; + incr(r); + best_mag:=approx_mag(font_name[nf],desired_mag); + m:=best_mag; + cur_name[r]:=xchr[ m div 1000 + xord['0']]; + incr(r); + cur_name[r]:=xchr[ (m div 100) mod 10 + xord['0']]; + incr(r); + cur_name[r]:=xchr[ (m div 10) mod 10 + xord['0']]; + incr(r); + cur_name[r]:=xchr[ m mod 10 + xord['0']]; + incr(r); + cur_name[r]:='/'; end +else + r:=0; + +for k:=font_name[nf] to font_name[nf+1]-1 do + begin incr(r); + if r+4>name_length then + abort('DVIplus capacity exceeded (max font name length=', + name_length:1,')!'); +@.DVIplus capacity exceeded...@> + cur_name[r]:=xchr[names[k]]; + end; +cur_name[r+1]:='.'; cur_name[r+2]:='P'; cur_name[r+3]:='X'; cur_name[r+4]:='L' + +@* Interpreting the {\tentex DVI} file. +The main work of \.{DVIplus} 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)|. +The |lj_h| and |lj_v| registers hold the current actual cursor position +of the printer. + +The stack of $(h,v,w,x,y,z)$ values is represented by eight arrays +called |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|. + +@= +@!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values} +@!lj_h,@!lj_v:integer; {current cursor position} +@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack: + array [0..stack_size] of integer; {pushed down values in \.{DVI} units} +@!hhstack,@!vvstack: + array [0..stack_size] of integer; {pushed down values in pixels} + +@ Three characteristics of the pages (their |max_v|, |max_h|, and +|max_s|) are specified in the postamble, and a warning message +is printed if these limits are exceeded. Actually |max_v| is set to +the maximum height plus depth of a page, and |max_h| to the maximum width, +for purposes of page layout. Since characters can legally be set outside +of the page boundaries, it is not an error when |max_v| or |max_h| is +exceeded. But |max_s| should not be exceeded. + +The postamble also specifies the total number of pages; \.{DVIplus} +checks to see if this total is accurate. + +@= +@!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_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels} +@!total_pages:integer; {the stated total number of pages} +@!page_count:integer; {the total number of pages seen so far} + +@ @= +max_v:=@'17777777777-99; max_h:=@'17777777777-99; max_s:=stack_size+1;@/ +max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0; + +@ 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. + +@d four_cases(#)==#,#+1,#+2,#+3 +@d eight_cases(#)==four_cases(#),four_cases(#+4) +@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8) +@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16) +@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32) + +@p 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= +@!s:integer; {current stack size} +@!ss:integer; {stack size to print} +@!cur_font:integer; {current internal font number} +@!prev_font:integer; +@!fonts_in_use:integer; {how many are currently loaded} +@!fonts_on_page:integer; {how many fonts used on current page} + +@ @= +fonts_in_use:=0; + +@ Here is the overall setup. + +@p @t\4@>@@; +procedure do_page; +label fin_set,fin_rule,move_right,done,exit; +var o:eight_bits; {operation code of the current command} +@!p,@!q:integer; {parameters of the current command} +pp,qq:integer; +i,j,k:integer; +@!a:integer; {byte number of the current command} +@!hhh:integer; {|h|, rounded to the nearest pixel} +@!height,pitch:i2c; +begin cur_font:=nf; {set current font undefined} +prev_font:=-1; +s:=0; h:=round(h_offset/conv); v:=round(v_offset/conv); +w:=0; x:=0; y:=0; z:=0; hh:=pixel_round(h); vv:=pixel_round(v); +lj_h:=-10000; lj_v:=-10000; + {initialize the state variables} +while true do @; +exit: +end; + +@ + +@d error(#)==print_ln('% ',#) + +@= +begin a:=cur_loc; +o:=get_byte; p:=first_par(o); +if eof_dvi_file then bad_dvi('file ended prematurely'); +@.the file ended prematurely@> +@; +fin_set: @; +fin_rule: @; +move_right: @; +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. + +@= +if o +else case o of + four_cases(set1), + four_cases(put1): bad_dvi('illegal character (', p:1, ')'); + set_rule, + put_rule: goto fin_rule; + @t\4@>@@; + @t\4@>@@; + othercases if special_cases(o,p,a) then goto done + else bad_dvi(' ') + endcases + +@ @= +function special_cases(@!o:eight_bits;@!p,@!a:integer):boolean; +label change_font,move_down,done,9998; +var q:integer; {parameter of the current command} +q1,q2:integer; {dummies for |fnt_def|} +@!k:integer; {loop index} +@!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?} +@!pure:boolean; {is the command error-free?} +@!vvv:integer; {|v|, rounded to the nearest pixel} +begin pure:=true; +case o of +@t\4@>@@; +@t\4@>@@; +four_cases(xxx1): @; +pre: begin error('preamble command within a page!'); goto 9998; + end; +@.preamble command within a page@> +post,post_post: begin error('postamble command within a page!'); goto 9998; +@.postamble command within a page@> + end; +othercases begin error('undefined command ',o:1,'!'); + goto done; +@.undefined command@> + end +endcases; +move_down: @; +change_font: @; +9998: pure:=false; +done: special_cases:=pure; +end; + +@ @= +nop: goto done; +bop: begin error('bop occurred before eop'); bad_dvi(' '); +@.bop occurred before eop@> + end; +eop: begin + write_lj(#12); + if s<>0 then error('stack not empty at end of page (level ', + s:1,')!'); +@.stack not empty...@> + return; + end; +push: begin + if s=max_s_so_far then + begin max_s_so_far:=s+1; + if s=max_s then error('deeper than claimed in postamble!'); +@.deeper than claimed...@> +@.push deeper than claimed...@> + if s=stack_size then + abort('DVIplus 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); ss:=s-1; goto done; + end; +pop: begin + if s=0 then error('(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]; + end; + ss:=s; 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==begin + 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; +end + +@= +four_cases(right1):out_space; +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==begin + if abs(p)>=5*font_space[cur_font] then vv:=pixel_round(v+p) + else vv:=vv+pixel_round(p); + goto move_down; +end + +@= +four_cases(down1):out_vmove; +y0,four_cases(y1):begin y:=p; out_vmove; + end; +z0,four_cases(z1):begin z:=p; out_vmove; + end; + +@ @= +sixty_four_cases(fnt_num_0), +four_cases(fnt1): + goto change_font; +four_cases(fnt_def1): begin + @; + goto done; + end; + +@ @= +q:=signed_quad; q:=signed_quad; q:=signed_quad; +q1:=get_byte; q2:=get_byte; +for k:=1 to q1+q2 do q:=get_byte; + +@ @= +begin bad_char:=false; +for k:=1 to p do + begin q:=get_byte; + if not ((q>=" ")and(q<="~")) then + bad_char:=true; + end; +if bad_char then error('non-ASCII character in xxx command!'); +@.non-ASCII character...@> +goto done; +end + +@ @= +goto fin_set + +@ This is the code that checks whether the next character to be +printed in |cur_font| has occurred previously. +If not, the character data is downloaded to the printer. If the character +is too large, the data is sent as a raster image. +If |cur_font| is also new, the font header is first downloaded. +The number of fonts stored in the printer is kept at |max_printer_fonts| +maximum. When more fonts are needed, the least recently used one is +deleted. + +The value of |max_printer_fonts| was choosed by waving a magig rod; +it would be more appropriate to calculate exactly how many bytes of +the user-available memory in the LaserJet+ is in use, and base the +decisions whether we must delete some fonts on that. +(Why can't you ask the printer how much memory it has left?). + +There should also be a test whether the number of fonts on a page +exceeds the maximum 16. + +@= +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>127) then q:=invalid_width +else q:=char_width(cur_font)(p); +if q=invalid_width then + begin error('character ',p:1,' invalid in font '); +@.character $c$ invalid...@> + print_font(cur_font); + if cur_font<>nf then print('!'); {font |nf| has `\.!' in its name} + end; +if font_used_on[cur_font]=0 then begin + if fonts_in_use=max_printer_fonts then + @; + incr(fonts_on_page); + @ end +else if font_used_on[cur_font]max_fonts_on_page then + error('too many fonts on this page'); +font_used_on[cur_font]:=page_count; +if cur_font <> prev_font then + write_lj(@=#27'('@>,font_num[cur_font]:1,'X'); +prev_font:=cur_font; +if char_status(cur_font)(p) = too_large then + raster_char(p) +else begin + if char_status(cur_font)(p) = not_loaded then + download_char(p); + if char_status(cur_font)(p) > 0 then begin + vv:=vv+char_status(cur_font)(p); + update_pos; + write_lj(vis_chr(p)); + vv:=vv-char_status(cur_font)(p); end + else begin + update_pos; + write_lj(vis_chr(p)); + end; + lj_h:=lj_h+char_pixel_width(cur_font)(p); +end; +if o>=put1 then begin + hh:=hh-char_pixel_width(cur_font)(p); + goto done; +end; +if q=invalid_width then q:=0 +else hh:=hh+char_pixel_width(cur_font)(p); +goto move_right + +@ @= +begin + height.i0:=4*round(font_design_size[cur_font]* + font_mag[cur_font]/1000.0*conv); + if (height.i0 < 0) or (height.i0>10922) then + height.i0:= 10922; + pitch.i0:=height.i0-20; {is this value used for anything in the printer??} + + write_lj(@=#27'*c'@>,font_num[cur_font]:1, + {char cell 255*255 pixels max} + {baseline distance set to $255-$|baseline| } + @='D'#27')s26W'#0#26#0#1#0#0#0@>, chr(255-baseline), + @=#0#255#0#255#0#1#1#21@>, + pitch.c[0], pitch.c[1], height.c[0], height.c[1], + @=#0#0#0#0#0#0#27'*c4F'@>); + incr(fonts_in_use); +end + +@ @= +begin + j:=9999; + k:=nf; + for i:=0 to nf-1 do + if font_used_on[i] < j then begin + j:=font_used_on[i]; + k:=i; + end; + write_lj(@=#27'*c'@>,font_num[k]:1,'d2F'); + font_used_on[k]:=0; + for i:=0 to 127 do + if (char_status(k)(i) > 0) or (char_status(k)(i) = loaded_ok) then + char_status(k)(i):=not_loaded; + decr(fonts_in_use); +end + +@ @= +q:=signed_quad; +qq:=rule_pixels(q); +pp:=rule_pixels(p); +if (p>0) and (q>0) then begin + vv:=vv-pp; + update_pos; + write_lj(@=#27'*c'@>,qq:1,'a',pp:1,'b0P'); + vv:=vv+pp; +end; +if o=put_rule then goto done; +hh:=hh+qq; +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. \.{DVIplus} ensures that the +amount of drift will never exceed |max_drift| pixels. + +Since \.{DVIplus} is intended to diagnose strange errors, it checks +carefully to make sure that |h| and |v| do not get out of range. +Normal \.{DVI}-reading programs need not do this. + +@d infinity==@'17777777777 {$\infty$ (approximately)} +@d max_drift=2 {we insist that abs|(hh-pixel_round(h))<=max_drift|} + +@= +if (h>0)and(q>0) then if h>infinity-q then + begin error('arithmetic overflow! parameter changed from ', +@.arithmetic overflow...@> + q:1,' to ',infinity-h:1); + q:=infinity-h; + end; +if (h<0)and(q<0) then if -h>q+infinity then + begin error('arithmetic overflow! parameter changed from ', + q:1, ' to ',(-h)-infinity:1); + q:=(-h)-infinity; + end; +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; +end; +h:=h+q; +if abs(h)>max_h_so_far then + begin if abs(h)-round(h_offset/conv)>max_h+99 then + begin error('warning: h > ',max_h:1,'!'); +@.warning: |h|...@> + max_h:=abs(h); + end; + max_h_so_far:=abs(h); + end; +goto done + +@ @= +if (v>0)and(p>0) then if v>infinity-p then + begin error('arithmetic overflow! parameter changed from ', +@.arithmetic overflow...@> + p:1,' to ',infinity-v:1); + p:=infinity-v; + end; +if (v<0)and(p<0) then if -v>p+infinity then + begin error('arithmetic overflow! parameter changed from ', + p:1, ' to ',(-v)-infinity:1); + p:=(-v)-infinity; + end; +vvv:=pixel_round(v+p); +if abs(vvv-vv)>max_drift then begin + if vvv>vv then vv:=vvv-max_drift + else vv:=vvv+max_drift; +end; +v:=v+p; +if abs(v)>max_v_so_far then + begin if abs(v)-round(v_offset/conv)>max_v+99 then + begin error('warning: v > ',max_v:1,'!'); +@.warning: |v|...@> + max_v:=abs(v); + end; + max_v_so_far:=abs(v); + end; +goto done + +@ @= +begin + font_num[nf]:=p; cur_font:=0; + while font_num[cur_font]<>p do incr(cur_font); + goto done +end + +@* Using the backpointers. +First comes a routine that illustrates how to find the postamble quickly. + +@= +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<>dvi_id 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 another one that we will need shortly. + +@= +@!post_loc:integer; {byte location where the postamble begins} +@!first_backpointer:integer; {the pointer following |post|} +@!start_loc:integer; {byte location of the first page to process} +@!start_inx:integer; {index into |page_start| for first page} +@!last_loc:integer; {byte localtion of last page to process} +@!page_start:array[1..max_bops] of integer; {pointers to |bop|s} + +@ The next routines follow the backpointers +to move through a \.{DVI} file in reverse order. \.{DVIplus} does this because +it wants to print the pages backwards as the LaserJet stacks +them with the printed side up. + +First we search for the starting page and the last page. + +@= +q:=post_loc; p:=first_backpointer; start_loc:=-1; +repeat + {now |q| points to a |post| or |bop| command; |p>=0| is prev pointer} + if p>q-46 then + bad_dvi('page link ',p:1,' after byte ',q:1); +@.page link wrong...@> + q:=p; move_to_byte(q); + k:=get_byte; + if k=bop then incr(page_count) + else bad_dvi('byte ',q:1,' is not bop'); +@.byte n is not bop@> + if page_count>max_bops then bad_dvi('there are too many pages'); +@.there are too many pages@> + page_start[page_count]:=q; + for k:=0 to 9 do count[k]:=signed_quad; + if start_match then begin start_loc:=q; start_inx:=page_count; end; + p:=signed_quad; +until p<0; +if start_loc<0 then abort('starting page number could not be found!'); +@.starting page number...@> +if (start_inx > max_pages) then + last_loc:=page_start[start_inx-max_pages+1] +else + last_loc:=page_start[1]; +if page_count<>total_pages then + print_ln('there are really ',page_count:1, + ' pages, not ',total_pages:1,'!'); +@.there are really n pages@> + +@ This is the code that really goes through the pages to be printed +(in reverse order). It starts from the page pointed to by |last_loc| and +proceeds up to |start_loc|. + +The code shown here uses a convention that has proved to be useful: +If the starting page was specified as, e.g., `\.{1.*.-5}', then +all page numbers in the file are displayed by showing the values of +counts 0, 1, and~2, separated by dots. Such numbers can, for example, +be displayed on the console of a printer when it is working on that +page. + +@= +page_count:=0; +q:=last_loc; +repeat + move_to_byte(q); k:=get_byte; + incr(page_count); + fonts_on_page:=0; + for k:=0 to 9 do count[k]:=signed_quad; + q:=signed_quad; + print('['); + for k:=0 to start_vals do + begin print(count[k]:1); + if k +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;@/ +if total_pages>max_bops then + bad_dvi('enormous number of pages (', total_pages:1, ')'); +@.enormous number of pages@> +@; +@; +end; + +@ No warning is given when |max_h_so_far| exceeds |max_h| by less than~100, +since 100 units is invisibly small; it's approximately the wavelength of +visible light, in the case of \TeX\ output. Rounding errors can be expected +to make |h| and |v| slightly more than |max_h| and |max_v|, every once in +a~while; hence small discrepancies are not cause for alarm. + +@ When we get to the present code, the |post_post| command has +just been read. + +@= +q:=signed_quad; +if q<>post_loc then + print_ln('bad postamble pointer in byte ',cur_loc-4:1,'!'); +@.bad postamble pointer@> +m:=get_byte; +if m<>dvi_id then print_ln('identification in byte ',cur_loc-1:1, +@.identification...should be n@> + ' should be ',dvi_id:1,'!'); +k:=cur_loc; m:=223; +while (m=223)and not eof_dvi_file do m:=get_byte; +if not eof_dvi_file then bad_dvi('signature in byte ',cur_loc-1:1, +@.signature...should be...@> + ' should be 223') +else if cur_loc + cur_loc-k:1,')'); + +@ @= +repeat k:=get_byte; +if (k>=fnt_def1)and(knop; +if k<>post_post then + print_ln('byte ',cur_loc-1:1,' is not postpost!') +@.byte n is not postpost@> + +@* The main program. +Now we are ready to put it all together. This is where \.{DVIplus} starts, +and where it ends. + +@p begin dont_catch_errors; initialize; {get all variables initialized} +dialog; {set up all the options} +@; +@; +read_postamble; +print_ln('Total of ', nf:1, ' fonts.'); +@; +write_lj(@=#27'E'#27'&l'@>, copies:1, @='X'#27'*t300R'@>); +@; +write_lj(@=#27'&l1X'@>); +write_ln(laser_file,'_'); +print_nl +end. + +@ The main program needs a few global variables in order to do its work. + +@= +@!k,@!m,@!n,@!p,@!q:integer; {general purpose registers} + +@ A \.{DVI}-reading program that reads the postamble first need not look at the +preamble; but \.{DVIplus} looks at the preamble in order to do error +checking, and to display the introductory comment. + +@= +if not open_dvi_file then + bad_dvi('cannot open 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<>dvi_id then + print_ln('identification in byte 1 should be ',dvi_id:1,'!'); +@.identification...should be n@> +@; +p:=get_byte; {fetch the length of the introductory comment} +print(''''); +while p>0 do + begin decr(p); print(xchr[get_byte]); + end; +print_ln('''') + +@ The conversion factor |conv| is figured as follows: There are exactly +|n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch, +and |resolution| pixels per inch. Then we have to adjust this +by the stated amount of magnification. + +@= +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 sho diff --git a/dviware/laserjet/readme b/dviware/laserjet/readme new file mode 100644 index 0000000000..7b9d18416b --- /dev/null +++ b/dviware/laserjet/readme @@ -0,0 +1,22 @@ +This file is presented here just as it was received from +Finland. There is a considerable amount of system-dependent +code in it which would be better in a change file. Also, +it runs the LaserJet+ directly, rather than through the +creation of an intermediate file. But it is said to work, +and that is better than we had before. + +It is bound to need substantial revision before it will +work under 4.2/3 BSD Unix. Ideally, this work should +involve the elimination of the obvious system dependencies from +the web file, and the creation of a proper change file. + +Also, it is probably preferable to create the usual sort of +intermediate file, rather than to attempt to run the LaserJet +as an online device. + +If you are in the mood to make the change from PXL to GF +fonts, please do. And let us know about it when you are +satisfied with the results. + +Best of luck. + -- cgit v1.2.3