|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T U
Length: 231282 (0x38772) Types: TextFile Notes: Uncompressed file
└─⟦060c9c824⟧ Bits:30007080 DKUUG TeX 2/12/89 └─⟦ec53ceda6⟧ »./DVIware/obsolete/symbolics.sh.Z« └─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12 └─⟦af5ba6c8e⟧ »unix3.0/DVIWARE.tar.Z« └─⟦ca79c7339⟧ └─⟦ec53ceda6⟧ »DVIware/obsolete/symbolics.sh.Z« └─⟦this⟧
#! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # symbolics # This archive created: Fri Mar 11 16:07:32 1988 export PATH; PATH=/bin:$PATH if test ! -d 'symbolics' then mkdir 'symbolics' fi cd 'symbolics' if test -f 'NOTICE' then echo shar: will not over-write existing file "'NOTICE'" else cat << \SHAR_EOF > 'NOTICE' The symbolics driver is obsolete and not maintained. Source code can be found in the symbolics.sh file in ../ If you need it uncompress symbolics.sh and run it through sh. These empty directories are here for your convenience. SHAR_EOF fi # end of overwriting check if test -f '4.2-setup' then echo shar: will not over-write existing file "'4.2-setup'" else cat << \SHAR_EOF > '4.2-setup' # /bin/sh # set the symbolics area up for 4.2bsd cp 4.2Makefile Makefile SHAR_EOF fi # end of overwriting check if test -f 'dvi2lgp.web' then echo shar: will not over-write existing file "'dvi2lgp.web'" else cat << \SHAR_EOF > 'dvi2lgp.web' % Copyright 1983 by Richard Furuta and Pierre MacKay. Copying without % fee is permitted provided that the copies are not made or distributed % for direct commercial advantage and credit to the source is given. % Altered versions of this program are not to be copied % or distributed without permission from the authors. % There are no restrictions on the use of this program. % % This program is based on the DVItype program by D. E. Knuth. % CHECKME flags an uncertain part of the code % --fixme-- flags things that need attending to % % Remember to change the Version number both on the TeX title page and also % within the body of the program (see the definition for ``banner'') % There's a date on the banner, too. % History of modifications. % First working version (6 April, 1983), modestly called version 0.8. % Version 1 (11 April 1983) includes provision for LGP vectors. % Version 1.1 (11 May, 1983) changes ``write'' to ``tprint'' to % make the creation of a UNIX change file easier. Also made % the ``errors only'' production run less informative, changed the % old ``errors only'' run so that it is now identified as ``terse'' % and changed the name of the old ``terse'' run to ``some_debug.'' % Set a threshold to keep the right margin overrun warnings % from coming through on trivial numbers of scaled points. On % a production run, the threshold is half a pixel-width. % And here is a special treat. On production runs (and also % in ``terse'' mode), the default order of reading is from back % to front, so that the output pages get stacked in the right % order on the printer tray. How's that! This default can be % changed by the use of an option switch. % Version 1.2 (August 21, 1983) [rkf] explicitely specify what factor % to use when multiplying by certain well known magnifications. This % change required due to round-off errors and the new magstep % magnifications. % Version 1.9 (October 3, 1983) [rkf] updates things so that they work with % the latest versions of Tangle (2.0), Weave (2.2), and TeX (0.9999). % This version also supports a number of bells and whistles---parsing % the command line, suggesting a following command, \special{s}, and % \special{v}. (Changed below to more specific forms, e.g. \special{vector}) % Concurrently, [pam] fixes a bug in the way the default % mode handles a file of pages which all return |start_match| true. % and also a much more serious bug which causes some rules to be % badly positioned. Additional feature, big_char_threshold added % to provide for ephemeral loading of oversize characters (so % that LGP bit-map doesn't run out of space. % Version 1.91 (October 24) magsteps provided for magnification as an % alternative to explicit numeric values. Mysterious bug % in routine for -n:m switch cured. This selective % partial printout option works only in so-called |backwards| % mode, which should be satisfactory. [pam] % Version 1.92 (November 14, 1983) Several bug fixes, in |print_octal| % and in the simple sequential count routine. New switch added % to allow for rotation of the output into ``landscape'' mode, a % feature which will be most useful for transparencies. A % small amount of cleanup in the \.{WEAVE} formatting, and the % creation of a new starred module, to make room for a full % description of the program's operation in the introduction. [pam] % Updated code to correspond with dvitype, version 2. [rkf] % Version 2.0 (December 7, 1983). Landscape mode for huge characters % corrected. LGP \specials given more specific names. Short % manual for use added to starred Introduction module. Much % cleanup of WEAVE formatting. [pam] Adjustments to manual [rkf] % Version 2.1 (December 31, 1983). The zero-width circle font in % Lamport's LaTeX uncovered a bug which needed fixing. [pam] % Changed initial message about entering CR for default values % to make it clearer (from "no magnification" to % "postamble-specified magnification"). Redid the help messages % for the maximum page value. Modified prompts to begin with ">" [rkf] % Slight change once again in the maximum page value messages. % Made page_default_maximum into a WEB definition. Better % error trapping in the use of the |sw_c|n:m switch (a negative % page count will be converted to the default). [pam] % Version 2.2 (March 9, 1984). Print the name of the problem font % in the error message referring to that font. A null option switch % causes the question to be restated. Switches are forced to upper % case without regard to which operating system is being used. [rkf] % % Here is TeX material that gets inserted after \input webmac \def\hang{\hangindent 3em\indent\ignorespaces} \font\ninerm=amr9 \let\mc=\ninerm % medium caps for names like PASCAL \def\PASCAL{{\mc PASCAL}} \def\(#1){} % this is used to make module names sort themselves better \def\9#1{} % this is used for sort keys in the index \def\title{DVI2LGP} \def\contentspagenumber{1} \def\topofcontents{\null \titletrue % This really is a title page \vfill \centerline{\titlefont The {\ttitlefont DVI2LGP} processor} \centerline{(Version 2.2)} \vskip 15pt \centerline{R. Furuta and P. MacKay} \centerline{Department of Computer Science} \centerline{University of Washington} \centerline{Seattle, Washington 98195} \vskip 15pt \centerline{(March 9, 1984)} \vfill} \def\botofcontents{\vfill \centerline{\hsize 5.5in\baselineskip9pt \vbox{\ninerm\noindent The preparation of this report was supported in part by grants from Bell-Northern Research and Northern Telecom, Inc. to the University of Washington. `\TeX' is a trademark of the American Mathematical Society.}}} \pageno=\contentspagenumber \advance\pageno by 1 @* Introduction. The \.{DVI2LGP} utility program reads binary device-independent (``\.{DVI}'') files that are produced by document compilers such as \TeX, and converts them into a preprocessed \.{LGP} file in ``native-mode'' coding. This program has two chief purposes: (1)~It assembles and outputs a sequential file of \.{LGP} codes, and (2)~It can be used to determine whether a \.{DVI} file is valid or invalid, since it retains the symbolic output form of its parent, \.{DVItype}. 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 follows its parent model very closely in the critical places where a bit of subtlety is involved. The parent program is the \.{DVItype} program designed by Donald Knuth and this descendant shamelessly borrows all possible modules from the \.{DVItype} \.{WEB} file. The |banner| string defined here should be changed whenever \.{DVI2LGP} gets modified. @d banner=='This is DVI2LGP, Version 2.2, last modified March 9, 1984' {printed when the program starts} @d copyright=='Copyright (C) 1983 by Richard Furuta and Pierre MacKay.' @ This \.{DVI2LGP} processor attempts first of all to provide a simple and direct method for interpreting \.{DVI} files for the Symbolics Inc. \.{LGP} printer. In the default mode, we assume that the user wants an entire file printed, at true size, and delivered to the output tray in correct order for reading. If the user finds the default mode insufficent, there is a wide range of options to produce various forms of standard \TeX\ output, as well as several forms of diagnostic output which can be used to check on the correctness of the \.{DVI} file. Finally, it is possible to call on certain special features of the \.{LGP} which are outside the normal functions of \TeX. These are not part of the \TeX\ typesetting system, but they may be added to an otherwise normal \.{DVI} file through the use of the \TeX\ \.{\\special} escape primitive. Such usage will, of course, be absolutely specific to the \.{LGP} printer, but a \.{DVI} file containing ``\.{\\special}'' sequences can safely be run through any properly designed \.{DVI} interpreter. The presence of the sequence will be noted in the diagnostic log file, but no action will be taken (unless the interpreter uses exactly the same \.{\\special} for some other purpose). @ In default mode, we assume that the \\{input filename} argument appears on the \\{command line} with the \\{program call name}. If the program senses the presence of the \\{input filename} on the \\{command line}, and finds no other arguments, flags or switch characters, the program begins at once and uses preset default values for all options. If the \\{input filename} already has the extension \.{.dvi}, this extension need not be supplied in the \\{command line}, since it is assumed as the default. Whatever the input filename may be, the program in all cases generates an \\{output filename} with the extension \.{.lgp} and creates this file on the user's \\{current working directory}, (even if the input file has been taken from some other directory or structure). Thus, the first stages of a {\mc UNIX} transaction will look like this: \smallskip\noindent \line{\.{This is DVI2LGP, Version $n$, last modified May 29, 1453}\hfil} \line{\.{[sample.dvi->sample.lgp]}\hfil} \medskip \noindent and the first stages of most other transactions will be similar, except that the file names may be folded into uppercase. After the program has started running in default mode, the \.{DVI} preamble banner line is displayed on the terminal from which the job was started, followed by a progress report which displays the byte-count in from the start of the file to mark the occurrence of each \.{DVI bop} byte. A completed job creating an \.{LGP} file for the printer looks like this: \smallskip\noindent \line{\.{\% dvi2lgp sample}\hfil} \line{\.{This is DVI2LGP, Version $n$, last modified May 29, 1453}\hfil} \line{\.{[sample.dvi->sample.lgp]}\hfil} \line{\.{' TeX output 1471.7.12:1200'}\hfil} \line{\.{9296: beginning of page 3}\hfil} \line{\.{5066: beginning of page 2}\hfil} \line{\.{42: beginning of page 1}\hfil} \line{\.{\%}\hfil} \medskip \noindent (For some systems we add a final prompt---generated by the program---so that all that is needed is to respond to it with a carriage return and the \.{LGP} file will be queued to the printer, assuming that your system has provided a protocol for this queuing operation). @ If the \\{command line} does not include the \\{input filename}, then the full range of options becomes accessible. The user is first given the opportunity to redirect the diagnostic output (including the font identifications and the beginning-of-page notifications) to a file. Then the program asks for an \\{input filename}, which may be supplied with or without the \.{.dvi} extension. As in the default mode, the \\{output filename} is automatically generated from the \\{input filename}, and the file is created on the user's \\{current working directory}. At this point the user has three choices: \medskip \item{1.} The remaining default options can again be selected by typing a carriage return in response to the prompt line, which reads: \smallskip\noindent \line {\.{> <CR> to accept default of all pages, postamble-specified magnification.}\hfil} \line{\.{ ? for help}\hfil} \smallskip \item{2.} A display of help messages can be called up by typing a question mark. \smallskip \item{3.} The full sequence of options can be started by typing any visible ASCII character other than a question mark. The first cluster of options must be specified all on the one line and, in the present version of the program, may not have any spaces between options. Each distinct option is introduced by a ``switch character'' which is established by the use of a \.{WEB} definition in this program (see the index, under |sw_c|). The following single-letter options exist: \medskip\noindent For position on the output sheet: \smallskip\hang \.{C}---\.{Center}. Equal margins right and left, as nearly as possible. This is the default. \smallskip\hang \.{L}---\.{Left}. Text pushed over to the left margin as close as the \.{LGP} hardware allows. \smallskip\hang \.{R}---\.{Right}. Text pushed over to the right margin, leaving as wide a left margin as the \.{LGP} hardware allows. \medskip\noindent For orientation on the output sheet: \smallskip\hang \.{V}---\.{Vertical}. Normal, portrait mode output. This is the default. \smallskip\hang \.{H}---\.{Horizontal}. Landscape mode output rotated ninety degrees. \medskip\noindent For sequential output in \.{DVI} order (which will stack up on the tray in inverted order): \smallskip\hang \.{I}---\.{Inverted}. The default has no name. \medskip\noindent For different amounts of diagnostic output : \smallskip\hang \.{P}---\.{Production}. The minimum described above as the default. \smallskip\hang \.{T}---\.{Terse}. Rather more information about fonts. \smallskip\hang \.{S}---\.{Some-debug}. Line by line reports of progress. \smallskip\hang \.{F}---\.{Full-debug}. Exhaustive, byte-by-byte reports. @ In addition to these, it is also possible to specify, on the same line, and with no intervening spaces, a simple selection of pages for output. This option (assuming that \.{`-'} is the switch character) takes the forms \.{-$n$} or \.{-$n$:$m$} or \.{-$n$:end}. Thus, it is possible to select a single page only, or a range of pages, or a range of pages through to the end of the file. Note that this option does not use the values in the \TeX\ counters (which often contain page numbers). When you use this option, you are simply telling the program to count in sequentially from the first to the {$n$}th~page of the file, to start there and to continue processing until the {$m$}th~page of the file or the end of the file is reached. The use of this option automatically sets the I(nverted) mode switch. @ The remaining options have been taken over from \.{DVItype} and are treated rather differently. For one thing, there is a specific prompt for each option. When you see the prompt reading: \smallskip\noindent \line{\.{> Starting page (default = *)}\hfil} \smallskip\noindent you can make use of the specific values in \TeX's count registers (which will override any values set by use of the \.{-$n$} option). The way to do this is described in the chapter \&{Optional modes of output}, and that description need not be repeated here. After you have decided what you wish to do with that option, you have a second chance to tell the program how many pages you really want to generate on the printer. The default is conservatively set at a million, unless you have already used the \.{-$n$:$m$} option described above. The last, but perhaps the most interesting option allows you to magnify the entire output page by whatever magnification you think you can get away with. The limitation here is fonts. If you have fonts corresponding to really wierd percentages of magnification then you can use such values as a response to this option prompt. Normally you will want to use \.{magsteps} instead. The principle behind \.{magsteps} is part of \TeX\ itself, and you can look it up in the \TeX book. @ The use of \.{\\special} functions is really part of \TeX\ rather than part of this driver. We have not completed the work on these yet, but the \.{LGP} vector-drawing function is available. For this, you should include the \TeX\ commands \.{\\special\{begin-vector\}} at the point where you want the vector to begin, and later on give the command \.{\\special\{vector\}}. For obvious reasons, these commands have to be paired on the same page. But don't worry if the paging operation of \TeX\ happens to split them up unexpectedly; the operations will simply be cancelled, and you will be told about it. It would be nice to get \.{\\special\{overlay=}\\{filename}\.{\}} working, but at present we cannot test it, so it remains in skeletal form for now, as does \.{\\special\{raster-data=}\\{filename}\.{\}} and \.{\\special\{screen-image=}\\{filename}\.{\}}. @* General programming environment. This program is written in standard \PASCAL, except where it is necessary to use extensions; for example, \.{DVI2LGP} must read and write files whose names are dynamically specified, and that would be impossible in pure \PASCAL. All places where nonstandard constructions are used have been listed in the index under ``system dependencies.'' @!@^system dependencies@> One of the extensions to standard \PASCAL\ that we shall deal with is the ability to move to a random place in a binary file; another is to determine the length of a binary file. These features are needed for efficiency in reading the \.{DVI} file and are virtually essential for reading the \.{PXL} file. The only alternative would be to have a huge temporary storage buffer into which the entire \.{PXL}file could be read for processing. Another extension is to use a default |case| as in \.{TANGLE}, \.{WEAVE}, etc. @d othercases == others: {default for cases not listed explicitly} @d endcases == @+end {follows the default case in an extended |case| statement} @f othercases == else @f endcases == end @ One of the features that \.{DVI2LGP} inherits from \.{DVItype} is a wide range of symbolic output formats, some of which will be of little use except for debugging. For more information about these formats, or |out_mode|s as they will be termed, you must wait until the chapter on ``{\bf Optional modes of output.}'' We put the definitions of the modes here, however, so that we can have free use of them throughout the entire \.{WEB} file. @d errors_only=0 {minimal printing} @d terse=1 {abbreviated symbolic output} @d some_debug=2 {quite a bit of information} @d verbose=3 {lots and lots of detailed tracing} @ The binary input comes from |dvi_file|, and symbolic output can be written on \PASCAL's standard |output| file. The term |print| is used instead of |write| when this program writes on |output|, so that all such output can easily be redirected if desired. We use the terms |tprint| and |tprint_ln| for terminal output, in order to make any necessary change easier. The |term_in| and |term_out| files are defined later. @d print(#)==write(#) @d print_ln(#)==write_ln(#) @d tprint(#)==write(term_out,#) {|write| will work in most environments} @d tprint_ln(#)==write_ln(term_out,#) {|write_ln| likewise} @p @<Compiler directives@> program DVI2LGP(@!output); label @<Labels in the outer block@>@/ const @<Constants in the outer block@>@/ type @<Types in the outer block@>@/ var@?@<Globals in the outer block@>@/ @<Procedures for \.{LGP} specials@> procedure initialize; {this procedure gets things started properly} var i,j,k,l,m,n:integer; {local variables for initializations} begin tprint_ln(banner);@/ tprint_ln(copyright);@/ @<Set initial values@>@/ end;@/ @ Some of this code is optional for use when debugging only; such material is enclosed between the delimiters |debug| and $|gubed|$. Other parts, delimited by |stat| and $|tats|$, are optionally included if statistics about \.{DVI2LGP}'s memory usage are desired. @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} @f debug==begin @f gubed==end @# @d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering usage statistics} @d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering usage statistics} @f stat==begin @f tats==end @ The \PASCAL\ compiler used to develop this system has ``compiler directives'' that can appear in comments whose first character is a dollar sign. In production versions of \.{DVI2LGP} these directives tell the compiler that @^system dependencies@> it is safe to avoid range checks and to leave out the extra code it inserts for the \PASCAL\ debugger's benefit, although interrupts will occur if there is arithmetic overflow. There is also an directive to ensure that the ``\&{extended addressing}'' feature in TOPS-20 \PASCAL\ is included. @<Compiler directives@>= @{@&$X+,C-,A+,D-@} {extended adr., no range check, catch overflow, no debug} debug @{@&$X+,C+,D+@}@+ gubed {but turn everything on when debugging} @ If the program has to stop prematurely, it goes to the `|final_end|'. Another label, |done|, is used when stopping normally. @d final_end=9999 {label for the end of it all} @d done=30 {go here when finished with a subtask} @<Labels...@>=final_end,done; @ The following parameters can be changed at compile time to extend or reduce \.{DVI2LGP}'s capacity. @<Constants...@>= @!max_fonts=252; {maximum number of fonts allowed in current \.{LGP} format} @!line_length=79; {bracketed lines of output will be at most this long} @!terminal_line_length=150; {maximum number of characters input in a single line of input from the terminal} @!stack_size=100; {\.{DVI} files shouldn't |push| beyond this depth} @!name_size=12000; {total length of all font file names} @!name_length=50; {a file name shouldn't be longer than this} @!max_cached_fonts=10; {all that can be active at one time} @ In addition to these system-related constants, there are some which apply to the printer itself. The value of |big_char_threshold| is chosen to protect against any possible overflow in the byte-length arguments of the \.{LGP} ``Define a Small-Size Character'' command. The value of |huge_char_threshold| was chosen after inspection of a very much magnified \.{MATHEX} font (the actual example was \.{AMEX10.2986PXL}). When |huge_char_threshold| is set at 512, about half of this font will be passed into the font storage memory of the \.{LGP}, but the really oversize characters will be painted on the page bit-map directly, using the ``Define a Block of Raster Data'' command so that they do not crowd out other more useful font characters. With |huge_char_threshold| set at 512, the minimum size at which ``Define a Block of Raster Data'' takes over is .25~in.~by 1~in. @^Define a Small-Size Character@> @^Define a Block of Raster Data@> @<Const...@>= @!physical_margin=75; {$75$ bits reserved because \.{LGP} will clip them anyway} @!right_margin=75; {the same for the right side} @!full_width = 2040; {$8.5$ inches at 240 pixels/inch} @!normal_top=120; {half an inch seems to be a good working value} @!page_pixel_height=2640; {11 inches at 240 pixels/inch} @!threshold=5.0; {threshold before horizontal overrun reported is 5 pixels} @!big_char_threshold=128; {this protects against overflow of an 8-bit field} @!huge_char_threshold=512; {64 pixels wide and 256 pixels deep, for example} @ Certain features of this program require a ``clever'' operating system which will allow for such things as a rescan of the command line and a prompt (complete with argument) for the next process in sequence. We note the presence or absence of such features with the following flag, whose initialization can be changed in other versions of this program. We also set up for an eventual consideration of batch operation. @d batch_mode=0 @<Glob...@>= @!clever:boolean; {initialized to true if the command line can be rescanned} @ @<Set initial...@>= clever:=false; {assume that most implementations aren't that clever} @ Here are some macros for common programming idioms. @d incr(#) == #:=#+1 {increase a variable by unity} @d decr(#) == #:=#-1 {decrease a variable by unity} @d do_nothing == {empty statement} @ If the \.{DVI} file is badly malformed, the whole process must be aborted; \.{DVI2LGP} will give up, after issuing an error message about the symptoms that were noticed. Such errors might be discovered inside of subroutines inside of subroutines, so a procedure called |jump_out| has been introduced. This procedure, which simply transfers control to the label |final_end| at the end of the program, contains the only non-local |goto| statement in \.{DVI2LGP}. @^system dependencies@> @d abort(#)==begin print(' ',#); jump_out; end @d bad_dvi(#)==abort('Bad DVI file: ',#,'!') @.Bad DVI file@> @p procedure jump_out; begin goto final_end; end; @* The character set. Like all programs written with the \.{WEB} system, \.{DVI2LGP} 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 both \.{DVI} and \.{LGP} files use ascii code for file names, reference to characters in \.{PXL} fonts and certain other strings. The next few modules of \.{DVI2LGP} have therefore been copied from the analogous ones in the \.{WEB} system routines. They have been considerably simplified, since \.{DVI2LGP} need not print the ascii control codes with values less than @'40. If such codes appear in the \.{DVI} file, they will be printed as question marks. For the \.{LGP} file, however we must have access to the full range of ascii code, and not merely the graphic characters. @<Types...@>= @!ascii_code=0.."~"; {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 \.{DVI2LGP}. So we shall assume that the \PASCAL\ system being used for \.{DVI2LGP} has a character set containing at least the standard visible characters of ascii code (|"!"| through |"~"|). This is a very safe assumption in at least this version of the program. 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. We include here a \.{WEB} constant for case changes, which ought to work on any system in which lower-case letters can be folded into upper-case, (which is certainly true for both {\mc ASCII} and {\mc EBCDIC}). We also make life easier for {\mc UNIX} programmers, by using a macro to provide the characters for file-name extensions. If no folding is desired, just redefine |system_case_of| to leave out the case adjustment provided by |fold_upper| (i.e., make it |xchr[#]). @^ASCII@>@^EBCDIC@> @^system dependencies@> @d text_char == char {the data type of characters in text files} @d first_text_char=0 {ordinal number of the smallest element of |text_char|} @d last_text_char=127 {ordinal number of the largest element of |text_char|} @d case_adjust="a" - "A" {very useful for case changes in either direction} @d fold_upper(#)==xchr[#-case_adjust] {translate letter} @d system_case_of(#)==fold_upper(#) {leave out the adjustment for {\mc UNIX}} @<Types...@>= @!text_file=packed file of text_char; @ The \.{DVI2LGP} 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. @<Glob...@>= @!xord: array [text_char] of ascii_code; {specifies conversion of input characters} @!xchr: array [0..255] of text_char; {specifies conversion of output characters} @ Under our assumption that the visible characters of standard ascii are all present, the following assignment statements initialize the |xchr| array properly, without needing any system-dependent changes. @<Set init...@>= for i:=0 to @'37 do xchr[i]:='?'; xchr[@'40]:=' '; xchr[@'41]:='!'; xchr[@'42]:='"'; xchr[@'43]:='#'; xchr[@'44]:='$'; xchr[@'45]:='%'; xchr[@'46]:='&'; xchr[@'47]:='''';@/ xchr[@'50]:='('; xchr[@'51]:=')'; xchr[@'52]:='*'; xchr[@'53]:='+'; xchr[@'54]:=','; xchr[@'55]:='-'; xchr[@'56]:='.'; xchr[@'57]:='/';@/ xchr[@'60]:='0'; xchr[@'61]:='1'; xchr[@'62]:='2'; xchr[@'63]:='3'; xchr[@'64]:='4'; xchr[@'65]:='5'; xchr[@'66]:='6'; xchr[@'67]:='7';@/ xchr[@'70]:='8'; xchr[@'71]:='9'; xchr[@'72]:=':'; xchr[@'73]:=';'; xchr[@'74]:='<'; xchr[@'75]:='='; xchr[@'76]:='>'; xchr[@'77]:='?';@/ xchr[@'100]:='@@'; xchr[@'101]:='A'; xchr[@'102]:='B'; xchr[@'103]:='C'; xchr[@'104]:='D'; xchr[@'105]:='E'; xchr[@'106]:='F'; xchr[@'107]:='G';@/ xchr[@'110]:='H'; xchr[@'111]:='I'; xchr[@'112]:='J'; xchr[@'113]:='K'; xchr[@'114]:='L'; xchr[@'115]:='M'; xchr[@'116]:='N'; xchr[@'117]:='O';@/ xchr[@'120]:='P'; xchr[@'121]:='Q'; xchr[@'122]:='R'; xchr[@'123]:='S'; xchr[@'124]:='T'; xchr[@'125]:='U'; xchr[@'126]:='V'; xchr[@'127]:='W';@/ xchr[@'130]:='X'; xchr[@'131]:='Y'; xchr[@'132]:='Z'; xchr[@'133]:='['; xchr[@'134]:='\'; xchr[@'135]:=']'; xchr[@'136]:='^'; xchr[@'137]:='_';@/ xchr[@'140]:='`'; xchr[@'141]:='a'; xchr[@'142]:='b'; xchr[@'143]:='c'; xchr[@'144]:='d'; xchr[@'145]:='e'; xchr[@'146]:='f'; xchr[@'147]:='g';@/ xchr[@'150]:='h'; xchr[@'151]:='i'; xchr[@'152]:='j'; xchr[@'153]:='k'; xchr[@'154]:='l'; xchr[@'155]:='m'; xchr[@'156]:='n'; xchr[@'157]:='o';@/ xchr[@'160]:='p'; xchr[@'161]:='q'; xchr[@'162]:='r'; xchr[@'163]:='s'; xchr[@'164]:='t'; xchr[@'165]:='u'; xchr[@'166]:='v'; xchr[@'167]:='w';@/ xchr[@'170]:='x'; xchr[@'171]:='y'; xchr[@'172]:='z'; xchr[@'173]:='{'; xchr[@'174]:='|'; xchr[@'175]:='}'; xchr[@'176]:='~'; for i:=@'177 to 255 do xchr[i]:='?'; @ The following system-independent code makes the |xord| array contain a suitable inverse to the information in |xchr|. @<Set init...@>= for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40; for i:=" " to "~" do xord[xchr[i]]:=i; @* Device-independent file format. Before we get into the details of \.{DVI2LGP}, we need to know exactly what \.{DVI} files are. The form of such files was designed by David R. Fuchs in 1979. Almost any reasonable 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 list of all the commands that may appear in a \.{DVI} file. With each command we give its symbolic name (e.g., |bop|), its opcode byte (e.g., 129), 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 appropriate character number 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 \.{DVI2LGP} may eventually allow character codes greater than 255, assuming that they all have the same width as character 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$. Not even the Chinese language has this many characters, but this command might prove useful in some yet unforeseen way. \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]$ \dots $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} \dots \.{\\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. \.{DVItype} does not do such sorting, nor does \.{DVI2LGP}. Since the \.{LGP} device paints an entire page at once, it can skip this tedious step. \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 normal \.{\\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<63|; 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^{30}$@>|. \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. Since there are 72.27 points per inch and 2.54 centimeters per inch, and since \TeX82 works with scaled points where there are $2^{16}$ sp in a point, \TeX82 sets |num=25400000| and $|den|=7227\cdot2^{16}=473628672$. @^sp@> The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the desired magnification. The actual fraction by which dimensions are multiplied is therefore $mn/1000d$. Note that if a \TeX\ source document does not call for any `\.{true}' dimensions, and if you change it only by specifying a different \.{\\mag} setting, the \.{DVI} file that \TeX\ creates will be completely unchanged except for the value of |mag| in the preamble and postamble. (Fancy \.{DVI}-reading programs allow users to override the |mag|~setting when a \.{DVI} file is being printed.) Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not interpreted further. The length of comment |x| is |k|, where |0<=k<256|. @d id_byte=2 {identifies the kind of \.{DVI} files described here} @ Font definitions for a given font number |k| contain further parameters $$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$ The four-byte value |c| is the check sum that \TeX\ (or whatever program generated the \.{DVI} file) found in the \.{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, 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|[>=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 nest; 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 and output to binary files. We have seen that a \.{DVI} file is a sequence of 8-bit bytes. The bytes appear physically in what is called a `|packed file of 0..255|' in \PASCAL\ lingo. Packing is system dependent, and many \PASCAL\ systems fail to implement such files in a sensible way (at least, from the viewpoint of producing good production software). For example, some systems treat all byte-oriented files as text, looking for end-of-line marks and such things. Therefore some system-dependent code is often needed to deal with binary files, even though most of the program in this section of \.{DVI2LGP} is written in standard \PASCAL. @^system dependencies@> One common way to solve the problem is to consider files of |integer| numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to a sequence of four bytes $(a,b,c,d)$ using the following code, which avoids the controversial integer division of negative numbers: $$\vbox{\halign{#\hfil\cr |if x>=0 then a:=x div @'100000000|\cr |else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr \quad|end|\cr |x:=x mod @'100000000;|\cr |b:=x div @'200000; x:=x mod @'200000;|\cr |c:=x div @'400; d:=x mod @'400;|\cr}}$$ The four bytes are then kept in a buffer and output one by one. (On 36-bit computers, an additional division by 16 is necessary at the beginning. Another way to separate an integer into four bytes is to use/abuse \PASCAL's variant records, storing an integer and retrieving bytes that are packed in the same place; {\sl caveat implementor!\/}) It is also desirable in some cases to read a hundred or so integers at a time, maintaining a larger buffer. We shall stick to simple \PASCAL\ in this program, for reasons of clarity, even if such simplicity is sometimes unrealistic. @<Types...@>= @!eight_bits=0..255; {unsigned one-byte quantity} @!byte_file=packed file of eight_bits; {files that contain binary data} @ The program deals with three binary file variables: |dvi_file| is the main input file that we are translating and |lgp_file| is the output file in \.{LGP} preprocessed ``native-mode'' format. The font files, which we will describe later, are elements in an array |pxl_files|, which allows us access to several of the most active fonts at the same time. @<Glob...@>= @!dvi_file:byte_file; {the stuff we are printing} @!lgp_file:byte_file; {a file of \.{LGP} ``native-mode'' commands} @ To prepare these files for use, we |reset| or |rewrite| them. On any system which allows a rescan of the command line, the normal practice will be to give the |dvi_file| argument on that line, and even the alternative will involve a prompt for the user to supply a |dvi_file| name. We use the very mildly non-standard form of the |reset| statement here to open both the |dvi_file| and the |lgp_file|. The name of the |lgp_file| is generated from the essential part (before the extension) of the |dvi_file| name. The assumption here, therefore, is that |dvi_name| and |lgp_name| are set before |open_dvi_file| and |open_lgp_file| are called. @p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|} begin reset(dvi_file,dvi_name); cur_loc:=0; end; @# procedure open_lgp_file; {prepares \.{LGP} file for output} begin rewrite(lgp_file,lgp_name); end; @ The global variable |cur_loc| is the number of the byte about to be read next from |dvi_file|. @<Glob...@>= @!cur_loc:integer; {where we are about to look, in |dvi_file|} @ We shall use another set of simple functions to read the next byte or bytes from |dvi_file|. There are seven possibilities, each of which is treated as a separate function in order to minimize the overhead for subroutine calls. @^system dependencies@> @p function get_byte:integer; {returns the next byte, unsigned} var b:eight_bits; begin if eof(dvi_file) then get_byte:=0 else begin read(dvi_file,b); incr(cur_loc); get_byte:=b; end; end; @# function signed_byte:integer; {returns the next byte, signed} var b:eight_bits; begin read(dvi_file,b); incr(cur_loc); if b<128 then signed_byte:=b @+ else signed_byte:=b-256; end; @# function get_two_bytes:integer; {returns the next two bytes, unsigned} var a,@!b:eight_bits; begin read(dvi_file,a); read(dvi_file,b); cur_loc:=cur_loc+2; get_two_bytes:=a*256+b; end; @# function signed_pair:integer; {returns the next two bytes, signed} var a,@!b:eight_bits; begin read(dvi_file,a); read(dvi_file,b); cur_loc:=cur_loc+2; if a<128 then signed_pair:=a*256+b else signed_pair:=(a-256)*256+b; end; @# function get_three_bytes:integer; {returns the next three bytes, unsigned} var a,@!b,@!c:eight_bits; begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); cur_loc:=cur_loc+3; get_three_bytes:=(a*256+b)*256+c; end; @# function signed_trio:integer; {returns the next three bytes, signed} var a,@!b,@!c:eight_bits; begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); cur_loc:=cur_loc+3; if a<128 then signed_trio:=(a*256+b)*256+c else signed_trio:=((a-256)*256+b)*256+c; end; @# function signed_quad:integer; {returns the next four bytes, signed} var a,@!b,@!c,@!d:eight_bits; begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d); cur_loc:=cur_loc+4; if a<128 then signed_quad:=((a*256+b)*256+c)*256+d else signed_quad:=(((a-256)*256+b)*256+c)*256+d; end; @ Finally we come to the routines that assume the \PASCAL\ used for this program has the non-standard extension for random reading of files. The driver program below needs two such routines: |dvi_length| should compute the total number of bytes in |dvi_file|, possibly also causing |eof(dvi_file)| to be true; and |move_to_byte(n)| should position |dvi_file| so that the next |get_byte| will read byte |n|, starting with |n=0| for the first byte in the file. @^system dependencies@> Such routines are, of course, highly system dependent. They are implemented here in terms of two assumed system routines called |set_pos| and |cur_pos|. The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is negative or larger than the total number of items in |f|; in the latter case, |set_pos(f,n)| moves to the end of file |f|. The call |cur_pos(f)| gives the total number of items in |f|, if |eof(f)| is true; we use |cur_pos| only in such a situation. @p function dvi_length:integer; begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file); end; @# procedure move_to_byte(n:integer); begin set_pos(dvi_file,n); cur_loc:=n; end; @ The attempt to output |eight_bits| bytes is virtually certain to require some sort of system-dependent code. What we show here is far too simple-minded to work on any computer system we have access to, and it is intended primarily to provide a place for the appropriate routine in a \.{WEB} change file. @p procedure output_byte(b:integer); begin if b<0 then b:=b+@'400; {for an |eight_bits| value, remove sign} write(lgp_file,b); end; @^system dependencies@> \f @* Reading the font information. \.{DVI} file format does not include information about character widths, since that would tend to make the files a lot longer. But this program must know everything about the character, even the pattern of the bit-map used to paint it on the paper. \.{DVI2LGP} gets this information from \.{PXL} files. Full details about the organization of \.{PXL} files can be found in \\{TUGboat}, Volume 2 No. 3, pages 8-12. The elements of the Font Directory that most concern us here are the |tfm_width|s, which are recorded as the fourth word in a four word item associated with each character in the font. @ The following two routines are exactly analogous in their operation with the |dvi_file| routines |dvi_length| and |move_to_byte|, but they work on an element in an array of files, and it is simpler in every way to declare them as distinct functions. @p function get_pxl_length:integer; begin set_pos(pxl_files[active_font],-1); get_pxl_length:=cur_pos(pxl_files[active_font]); end; @# procedure pxl_move_to_byte(n:integer); begin set_pos(pxl_files[active_font],n); end; @^system dependencies@> @ Here is the array and its index. @<Glob...@>= @!pxl_files:array [1..max_cached_fonts] of byte_file; @!active_font:integer; {index into this array} @ When we are inputting from \.{PXL} files, it is usually preferable to read four bytes at a time. The input goes into global variables |b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3| the fourth. We also need |cur_name|, a string variable that will be set to the current \.{PXL} font file name before each of the elements in the |pxl_files| array is opened or |reset|. @<Glob...@>= @!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once} @!cur_name:packed array[1..name_length] of char; {external name, with no lower case letters} @ The |read_pxl_bytes| procedure sets |b0| through |b3| to the next four bytes in the current \.{PXL} file. @^system dependencies@> @p procedure read_pxl_bytes; begin read(pxl_files[active_font],b0); read(pxl_files[active_font],b1); read(pxl_files[active_font],b2); read(pxl_files[active_font],b3); end; @ Here is a routine to read a full word of data from |pxl_files| without any system dependent problems. @p procedure get_pxl_word(var a:integer); begin read_pxl_bytes; if b0 < 128 then a:=((b0*256+b1)*256+b2)*256+b3 else a:=(((b0-256)*256+b1)*256+b2)*256+b3; end; @ For purposes of this program, we need to know several things about a given character |c| in a given font |f|: (1)~Is |c| a legal character in~|f|? (2)~What are the dimensions of the rectangular character envelope of |c|? (3)~What offsets are needed to get to the top left (or, in rotated fonts, the top right) pixel position in the character envelope? (4)~What is the relative |tfm_width| of |c|---that is, how wide is this character relative to the EM-square design width of the font? We also need to know the symbolic name of each font, so it can be printed out, and we need to compute a value which we will call |font_space| (for which, see the index). At the time when each character is first encountered, we will also need to have access to its bit-map. The answers to these questions appear implicitly in the following data structures. The current number of known fonts is |nf|. Each known font has an internal number |f|, where |0<=f<nf|; the external number of this font, i.e., its font identification number in the \.{DVI} file, is |font_num[f]|, and the external name of this font is the string that occupies positions |font_name[f]| through |font_name[f+1]-1| of the array |names|. The latter array consists of |ascii_code| characters, and |font_name[nf]| is its first unoccupied position. A horizontal motion less than |font_space[f]| will be treated as a `kern' that is not indicated in the printouts that \.{DVI2LGP} produces between brackets when it is printing symbols for a diagnostic run. There are always 128 character positions in the current form of a \.{PXL} font, and even a null character could have a width (though we do not use this feature in normal practice). In practice, most fonts are quite full, so there is no point in trying to save space by closing up the zero-width entries for null characters. @d invalid_width==@'17777777777 @d pxl_mark = 1001 @<Glob...@>= @!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; {product of overall and ``at size'' values} @!font_space:array [0..max_fonts] of integer; {boundary between ``small'' and ``large'' spaces} @!nf:0..max_fonts; {the number of known fonts} @ @<Set init...@>= nf:=0; font_name[0]:=0; font_space[0]:=0; @ It is, of course, a simple matter to print the name of a given font. |print_font| is the normal routine. |print_budding_font| is used to print a font that is in the process of being defined. @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]+2 to font_name[f+1]-1 do print(xchr[names[k]]); end; end; procedure print_budding_font(f:integer); {|f| is an internal font number} var k:0..name_size; {index into |names|} begin if f>nf then print('UNDEFINED!') else begin for k:=font_name[f]+2 to font_name[f+1]-1 do print(xchr[names[k]]); end; end; @ In the \.{PXL} directory there are four words of data associated with each valid character: \item{1)}the |envelope|, which gives the exact width and height in pixels of each character of the font, \item{2)}the |offsets|, which give the X-offset and Y-offset (in pixels) from the top left corner of the |envelope| needed to reach the starting reference pixel of the character (which should be close to the point that \TeX\ would consider the base-line of that character), \item{3)}the |raster_ptr| pointer to the first line in an array of bit-maps provided for each character. (By the conventions of the current style in \.{PXL} files, these are loosely packed, with an integer number of words allowed for each line. But only the BigEndian $32$ bits are used.) \item{4)}the width of this character relative to the width of the EM-square design size of this font. (This value must correspond with the width value in the \.{TFM} file which \TeX\ associated with this font.) To save storage, we leave the two values which are represented in 16-bit fields in the 32-bit |envelope| alone. Since neither of them can be negative in a well-formed \.{PXL} file, it is easy to unpack them later, using \&{mod} and \&{div} and we will hope that our compiler has the \\{nous} to use a \\{shift} operation when it spots division by a power of 2. @<Type...@>= @!font_element = record @!envelope:integer; {this is actually two values, but neither can be $<0$} @!x_offset,@!y_offset:integer; @!raster_ptr:integer; {pointer to top raster ($-1$ if already processed)} @!tfm_width:integer; {slightly misleading; what goes here is computed from the genuine |tfm_width|} @!lgp_width:integer; {computed pixel width} end; @ @<Glob...@>= @!font_mem: array[0..max_fonts, 0..127] of font_element; @ An error message array is used to hold specific messages about errors in PXL input. The global variable |pxl_check_sum| is set to the check sum that appears in the current \.{PXL} file. @d error_size = 20 @<Glob...@>= @!error_in_pxl:packed array[1..error_size] of char; {\.{PXL} error messages} @!pxl_check_sum:integer; {check sum found in |pxl_files|} @ Here is a procedure that absorbs the necessary information from a \.{PXL} file, assuming that the file has just been successfully \&{reset} so that we are ready to read it. The procedure gives a few simple warnings if it detects anything amiss in the \.{PXL} data. @p function in_PXL(z: integer):boolean; {input \.{PXL} data or return |false|} label 9997, {go here when the format is bad} 9998, {go here when the information cannot be loaded} 9999; {go here to exit} var k:integer; {index for loops} @!alpha, @!beta: integer; {quantities used in the scaling computation} begin @<Read the \.{PXL ID} in the first word, then jump to the end of file and check the last five 32-bit values; |goto 9997| if there is a problem@>; @<Read the |offsets| and |tfm_width| values@>; in_PXL:=true; goto 9999; 9997: print_ln('---not loaded, PXL file is bad');print_ln(error_in_pxl); @.PXL file is bad@> 9998: in_PXL:=false; 9999: end; @ Here we look at several general parameters for the font, so that we can check them against the \.{TFM} values recorded in the |dvi_file|. @<Read the \.{PXL ID}...@>= error_in_pxl:=('PXL id word <> 1001!'); @.PXL id word <> 1001!@> get_pxl_word(pxl_id); if pxl_id <> pxl_mark then goto 9997; pxl_length:=get_pxl_length; pxl_pos:=pxl_length-20; {five words of four bytes each} pxl_move_to_byte(pxl_pos); {we count on the implied \&{get} here} get_pxl_word(pxl_check_sum); if pxl_check_sum = 0 then print_ln('PXL has 0 check-sum!'); @.PXL has 0 check-sum!@> error_in_pxl:=('Mag. DVI units <= 0!'); @.Mag. DVI units <= 0!@> get_pxl_word(pxl_mag_val); if pxl_mag_val <= 0 then goto 9997 else begin error_in_pxl:=('Design DVI units<=0!'); @.Design DVI units<=0!@> get_pxl_word(pxl_design_size); end; if pxl_design_size <= 0 then goto 9997 else begin error_in_pxl:=('No directory pointer'); @.No directory pointer@> get_pxl_word(pxl_directory_ptr); end; if pxl_directory_ptr <> (pxl_length div 4)-517 then begin print('Directory pointer should be ', (pxl_length div 4)-517:1); @.Directory pointer should be@> print_ln(', but file gives ', pxl_directory_ptr:1); goto 9997; end; pxl_directory_ptr:=pxl_directory_ptr * 4; {adjust for |byte_file|} error_in_pxl:=('No PXL EOF! Chaos! '); @.No PXL EOF! Chaos! @> get_pxl_word(pxl_id); if not eof(pxl_files[active_font]) then goto 9997; if pxl_id <> pxl_mark then print_ln('Bad final PXL ID word. Odd but trivial.') @.Bad final PXL ID word@> @^system dependencies@> @ There are a number of identifiers in that last module which haven't yet been provided for, so\dots @<Glob...@>= @!pxl_id:integer; @!pxl_length:integer; @!pxl_pos:integer; {for direct access reading of |pxl_files|} @!pxl_mag_val,@!pxl_design_size:integer; {in DVI units} @!pxl_directory_ptr:integer; @ The first really useful part of |in_PXL| is setting up the directory table of |offsets|, |raster_ptr|s and |tfm_width|s. We also calculate the |lgp_width| (in pixels) of each character, by applying the |conv| value to the |tfm_width|. @d half_shift==@'200000 {to place a value in upper half-word of 32-bit word} @<Read the |offsets|...@>= pxl_pos:=pxl_directory_ptr; pxl_move_to_byte(pxl_pos); {remember the implied \&{get} here} for k:=0 to 127 do begin with font_mem[nf,k] do begin get_pxl_word(envelope); read_pxl_bytes; x_offset:=(b0 * 256) + b1; y_offset:=(b2 * 256) + b3; if x_offset>@'77777 then x_offset:=x_offset-@'200000; {extend sign} if y_offset>@'77777 then y_offset:=y_offset-@'200000; {extend sign} get_pxl_word(raster_ptr); {the top line pointer value} raster_ptr:=raster_ptr*4; {adjust for |byte_file|} @<Read and convert the |tfm_width| values@>; lgp_width:=trunc((conv*tfm_width) + 0.5) end; end @^system dependencies@> @ The most critical part of |in_PXL| is the width computation, which involves multiplying the relative |tfm_width|s 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 \.{TFM} file represents a real quantity, indicating what fraction each valid character's width is of the |font_scaled_size| (which may $=$ |font_design_size|) and which, in its turn, gives the \.{EM-square} width, in \.{DVI} units, of the font in the current font definition. To avoid the inaccuracies caused by different system implementations of the \&{real} type, each of these real values 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. We have adapted this module from the code provided in an early release of \.{DVItype}, taking care to maintain precisely the same level of precision. Note particularly how the multiplications proceed from the low-order to the high-order bytes of the value, moving byte by byte, which ensures against overflow, and how the least significant bits are removed by integer division before they can affect the value in the more significant bytes. @<Read and convert...@>= @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>; begin error_in_pxl:='Bad width--byte0 bad'; read_pxl_bytes; tfm_width:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta; if b0>0 then if b0<255 then goto 9997 else tfm_width:=tfm_width-alpha; end @ @<Replace |z|...@>= begin alpha:=16*z; beta:=16; while z>=@'40000000 do begin z:=z div 2; beta:=beta div 2; end; end \f @* Font caching by history of use. Truly ``optimal'' systems of caching require a system of reference to future needs, as described in the Stanford University Computer Science Report {\mc STAN-CS-82-901}. \\{Optimal Font Caching} by David Fuchs and Donald Knuth. (March, 1982), but a reference to past history is a pretty good second choice, particularly when the output device has a very large internal memory for the storage of font characters. This system maintains a linked queue, to which each font is added until the queue is full. Each time a font that is already in the queue is |referenced| again, it is put down at the end of the queue. If the queue is full, and a new font must be added, the font at the head of the queue is removed to make room for it. Any font which does not get |referenced| frequently is likely to float to the head of the queue, and any font which is often |referenced| is likely to spend most of its time at or near the end of the queue, where it will not risk being bumped off. Alternatively, if a font has been very extensively used, it will eventually have contributed every needed character to the internal font memory of the \.{LGP}, and there will be no further reason for it to be |referenced|. @ @<Type...@>= @!cache_pointer=^cached_font; @!cached_font=record @!cache_number:1..max_cached_fonts; @!font_ptr:0..max_fonts; @!c_link:cache_pointer; end; @ @<Glob...@>= @!font_cache_list:cache_pointer; {list of open fonts} @ And now for the procedure that does all this. @d append_it=99 @p function open_font(this_font:integer):integer; label append_it; var @!cfn:integer; {temporary for cached font index number} @!fm:integer; {gets |font_mag| value for |this_font|} @!a:integer; {where |ascii_code| created from |font_mag| value} @!r:integer; {index into |cur_name|} @!k:integer; {index into |names|} @!p_f,@!q_f,@!referenced:cache_pointer; begin p_f:=font_cache_list; q_f:=p_f; referenced:=nil; cfn:=0; @<Set |q_f| to be the last element of the queue. If we find the entry for |this_font| along the way, put it in |referenced|.@>; if referenced<>nil then begin p_f:=referenced; goto append_it end; @<If there is still room, add to the queue, otherwise, bump an old entry off the head, so that its |cache_number| can be reused.@>; @<Move font name for |p_f| into the |cur_name| string and open the font file as one of the elements of the |pxl_files| array.@>; @<Append |p_f| to the end of the queue.@>; end; @ We begin by searching along the queue to find the end. If we find that our new font has already been |referenced|, we remove it from its current position in the queue for replacement at the end of the queue. Thus, the more frequently a font has been used, the more likely it is to be near the end of the queue, and the less likely it is to be bumped off the head of the queue by a newcomer. @<Set |q_f| to be...@>= while p_f<>nil do if p_f^.font_ptr=this_font then begin {dequeue |p|} referenced:=p_f; if p_f=font_cache_list then begin {was it at head of queue?} font_cache_list:=p_f^.c_link; {if so, bump it out} q_f:=p_f^.c_link {set |q_f| to head of queue too} end else q_f^.c_link:=p_f^.c_link; p_f:=p_f^.c_link end else begin q_f:=p_f; p_f:=p_f^.c_link; cfn:=cfn+1 {through the queue} end @ @<If there is still room...@>= if cfn<max_cached_fonts then begin new(p_f); p_f^.cache_number:=cfn+1 {set up a new |cache_number|} end else begin p_f:=font_cache_list; {we want to reuse this |cache_number|} font_cache_list:=font_cache_list^.c_link {bump out font at head of list} end @ An extension to standard \PASCAL\ is needed for file management since we want to associate each element in an array of files, |pxl_files|, with external files whose names are specified dynamically (i.e., not known at compile time). The following code assumes that `|reset(f,s)|' does this, when |f| is a file variable and |s| is a string variable that specifies the file name. If |eof(f)| is true immediately after |reset(f,s)| has acted, we assume that no file named |s| is accessible. The string |cur_name| is supposed to be set to the external name of the \.{PXL} or \.{VRL} file for the current font. This usually means that we need to prepend the name of the default directory, and to append the four digits which indicate the magnification applied to this font when it was created, and to add the suffix `\.{PXL}' or \.{VRL} after these. In most environments, we should also change lower case letters to upper case, since |cur_name| is a \PASCAL\ string. If |p=0|, i.e., if no font directory has been specified, \.{DVI2LGP} is supposed to use the default font directory, which is a system-dependent place where the standard fonts are kept. The string variable |default_directory| contains the name of this area. @^system dependencies@> @d default_directory_name=='TEXFONTS:' {change this to the correct name} @d default_directory_name_length=9 {change this to the correct length} @d extension_length=8 {length of the string (e.g.) ``\.{.1200PXL}''} @<Glob...@>= @!default_directory:packed array[1..default_directory_name_length] of char; @ @<Set init...@>= default_directory:=default_directory_name; @^system dependencies@> @ Now for the real work of opening the font file. @<Move font name...@>= for k:=1 to name_length do cur_name[k]:=' '; p:= names[font_name[this_font]]; n:=names[font_name[this_font]+1]; if p=0 then begin for k:=1 to default_directory_name_length do cur_name[k]:=default_directory[k]; r:=default_directory_name_length; end else r:=0; for k:=font_name[this_font]+2 to font_name[this_font+1]-1 do begin incr(r); if r+4>name_length then abort('DVI2LGP capacity exceeded (max font name length=', name_length:1,')!'); @.DVI2LGP capacity exceeded...@> if (names[k]>="a")and(names[k]<="z") then cur_name[r]:=system_case_of(names[k]) {a harmless do-nothing in {\mc UNIX}} else cur_name[r]:=xchr[names[k]]; end; fm:=font_mag[this_font]; cur_name[r+1]:='.'; if fm > 999 then begin a:=(fm div 1000)+ord('0'); cur_name[r+2]:=xchr[a]; fm:=fm mod 1000; end else decr(r); {for diminished fonts such as \.{999PXL}} a:=(fm div 100)+ord('0'); cur_name[r+3]:=xchr[a]; fm:=fm mod 100; a:=(fm div 10)+ord('0'); cur_name[r+4]:=xchr[a]; fm:=fm mod 10; a:=fm+ord('0'); cur_name[r+5]:=xchr[a]; cur_name[r+6]:=system_case_of("p"); cur_name[r+7]:=system_case_of("x"); cur_name[r+8]:=system_case_of("l"); if not take_defaults then begin if in_postamble then print(' == ') else if out_mode>errors_only then print(' requeue '); @.requeue (a previously queued font)@> if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then begin for k:=1 to r+extension_length do print(cur_name[k]);print_ln(' '); end; end; reset(pxl_files[p_f^.cache_number],cur_name); p_f^.font_ptr:=this_font @^system dependencies@> @ When we reach this point, we know that |p_f| is either a new addition to the font cache, or it is a previously |referenced| font, which has been removed from its old position in the list. @<Append |p_f| to the...@>= append_it: if q_f=nil then {This does the first font of all. It only happens once.} font_cache_list:=p_f else q_f^.c_link:=p_f; {|q_f| has been set to the end of the queue.} p_f^.c_link:=nil; {Show that |this_font| is the new end-of-queue} open_font:=p_f^.cache_number {return index into |pxl_files| array} @* Defining fonts. \.{DVI2LGP} reads the postamble first and loads all of the fonts defined there; then it processes the pages. In this case, a \\{fnt\_def} command should match a previous definition if and only if the \\{fnt\_def} being processed is not in the postamble. A global variable |in_postamble| is provided to tell whether we are processing the postamble or not. @<Glob...@>= @!in_postamble:boolean; {are we reading the postamble?} @ @<Set init...@>= in_postamble:=false; @ The |check_sum| in |tfm_file|s is recorded in a \.{PL} property list in octal notation, and it is helpful to be able to check errors against the information provided by \.{TFtoPL}, so if any |check_sum| discrepancies appear, we print the information in octal notation. This is not the same routine as the one in \.{PLtoTF} because, in this program, the value to be printed may already have been altered by sign-extension. The value of |kill_sign| is legitimate in \.{WEB} but may cause trouble in some environments. @^system dependencies@> @d kill_sign==(-@'17777777777-1) {so that we can have an unsigned value} @p procedure print_octal(@!a:integer); {outputs an integer in octal} var @!s:integer; {surrogate sign-bit} @!i:integer; {what we are working on} @!j:0..11; {one more than the number of digits of output} @!dig:array[0..10] of "0".."7"; {eleven digit maximum} begin print(' O '); {specify octal format} s:=0; if a < 0 then begin i:=a + kill_sign; s:= 2; end else i:=a; for j:=0 to 10 do dig[j]:="0"; j:=0; while (i>0)or(j=0) do begin dig[j]:=(i mod 8) + "0"; i:=i div 8; incr(j); end; if s>0 then begin dig[10]:=dig[10] + s; {restore lost ``sign-bit'' if any} j:=11; {picks up effects such as \.{O} 20000012345} end; repeat decr(j); print(xchr[dig[j]]); until j=0; end; @ In \TeX82, Versions 0.9999 and later, the concept of a ``magnification factor,'' based on factors of $\sqrt2$, was included. This meant that the \.{.PXL} files started getting odd numbered magnifications like 2074. Since these magnifications are really rounded values, we started failing to find fonts when the 1.2 device magnification factor was applied. For example, multiplying 2074 by 1.2 gave us 2489 rather than the proper 2488---a real drag. This function guesses what the proper magnification is, based on some knowledge of what the factors that might be generated are. @p function get_actual_mag(fm:integer): real; var rfm: real; begin if fm=1095 then rfm:=1.095445 {stephalf} else if fm=1315 then rfm:=1.314534 {stephalf above step \\{i}, needed for the \.{LGP} with its hidden step \\{i} mag} else if fm=2074 then rfm:=2.0736 {step \\{iv}} else if fm=2488 then rfm:=2.48832 {step \\{v}} else if fm=2986 then rfm:=2.985984 {step \\{vi}} else rfm:=fm/1000.0; {the remaining magnifications haven't been a problem} get_actual_mag:=rfm; end; @ The following subroutine does the necessary things when a \\{fnt\_def} command is being processed. @p procedure define_font(@!e:integer); {|e| is an external font number} var f:0..max_fonts; @!p:integer; {length of the area/directory spec} @!n:integer; {length of the font name proper} @!c,@!q,@!d:integer; {check sum, scaled size, and design size} @!rfm:real; {fudged font magnification as a real number} @!j,@!k:0..name_size; {indices into |names|} @!mismatch:boolean; {do names disagree?} begin if nf=max_fonts then abort('DVI2LGP capacity exceeded (max fonts=', max_fonts:1,')!'); @.DVI2LGP capacity exceeded...@> font_num[nf]:=e; f:=0; while font_num[f]<>e do incr(f); @<Read the font parameters into position for font |nf|, and print the font name@>; if in_postamble then begin if f<nf then print_ln('---this font was already defined!'); @.this font was already defined@> end else begin if f=nf then print_ln('---this font wasn''t loaded before!'); @.this font wasn't loaded before@> end; if f=nf then @<Load the \.{PXL} font, unless there are problems@> else @<Check that the current font definition matches the old one@>; end; @ @<Check that the current...@>= begin if font_check_sum[f]<>c then begin print_budding_font(f); print_ln('---check sum doesn''t match previous definition!'); @.check sum doesn't match@> end; if font_scaled_size[f]<>q then begin print_budding_font(f); print_ln('---scaled size doesn''t match previous definition!'); @.scaled size doesn't match@> end; if font_design_size[f]<>d then begin print_budding_font(f); print_ln('---design size doesn''t match previous definition!'); @.design size doesn't match@> end; j:=font_name[f]; k:=font_name[nf]; mismatch:=false; while j<font_name[f+1] do begin if names[j]<>names[k] then mismatch:=true; incr(j); incr(k); end; if k<>font_name[nf+1] then mismatch:=true; if mismatch then begin print_budding_font(f); print_ln('---font name doesn''t match previous definition!'); @.font name doesn't match@> end end @ We have to play a little trick here, owing to the fact that any given font may be \&{reset} more than once in the program. In order to regenerate the font file name correctly, we must store the length of the ``area'' string as well as the length of the basic ``name'' string for the font. Fortunately, since the normal contents of the |names| array is a subset of the integers in any case, we have no difficulty in putting these small integers in the same array. Another, very important trick appears in the evaluation of |font_mag|. \.{PXL} fonts came into being with the assumption that they were intended for 200-dot/inch devices, but the \.{LGP} we are preparing this file for is a 240-dot/inch device. Therefore, we want \.{1200PXL} fonts for true size output, and we want a 1.2 magnification over and above any font magnification specified in the font definition in the \.{DVI} file. However, as we discovered in the first run of this program, we most emphatically do \\{not} want to magnify anything but the font by this extra factor, so we plug it in quietly to the evaluation of |font_mag|. Finally, since the \\{magstep} values in |font_scaled_size| are already rounded as they appear in the \.{DVI} file, we also have the difficulty that two successive stages of rounding (one in the \TeX\ processor and one here) can produce undesirable values and create references to nonexistent fonts. The |get_actual_mag| function ``regenerates'' the |real| values corresponding to the various \\{magstep}s with just enough precision to obviate this twice-rounded effect. @<Read the font parameters into position for font |nf|...@>= c:=signed_quad; font_check_sum[nf]:=c;@/ q:=signed_quad; font_scaled_size[nf]:=q;@/ d:=signed_quad; font_design_size[nf]:=d;@/ rfm:=get_actual_mag(round(((q*1.0)/(d*1.0))*1000)); font_mag[nf]:=round(rfm*get_actual_mag(mag)*1.2*1000); {``at size'' magnification * overall magnification--note the $1.2$ for \.{LGP}} p:=get_byte; n:=get_byte; if font_name[nf]+n+p+2>name_size then abort('DVI2LGP capacity exceeded (name size=',name_size:1,')!'); @.DVI2LGP capacity exceeded...@> font_name[nf+1]:=font_name[nf]+n+p+2; if showing then print(': ') {when |showing| is true, the font number has already been printed} else if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then print('Font ',e:1,': '); if n+p=0 then print('null font name! Number ',e:1) @.null font name@> else begin names[font_name[nf]]:=p; names[font_name[nf]+1]:=n; for k:=font_name[nf]+2 to font_name[nf+1]-1 do names[k]:=get_byte; end; if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then print_budding_font(nf); {|out_mode| is explained in the next chapter.} @ @<Load the \.{PXL} font, unless there are problems@>= begin active_font:=open_font(f); if eof(pxl_files[active_font]) then begin print_budding_font(f); print('---not loaded, PXL file can''t be opened!') @.PXL file can\'t be opened@> end else begin if (q<=0)or(q>=@'1000000000) then begin print_budding_font(f); print('---not loaded, bad scale (',q:1,')!') @.bad scale@> end else if (d<=0)or(d>=@'1000000000) then begin print_budding_font(f); print('---not loaded, bad design size (',d:1,')!') @.bad design size@> end else if in_PXL(q) then @<Finish loading the new font info@>; end; if ((nf<>(f+1)) and (out_mode=errors_only)) then print_ln(' '); {provide a line-end for unterminated error messages in case of failure} end @ @<Finish loading...@>= 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_budding_font(nf); print_ln('---beware: check sums do not agree!'); @.beware: check sums do not agree@> print('('); print_octal(c); print(' in DVI vs. '); print_octal(pxl_check_sum);print_ln(' in PXL)'); end; if out_mode>errors_only then print('---defined in postamble at size ',q:1,' DVI units'); {|out_mode| is explained in the next chapter.} @.defined in postamble at size@> d:=trunc((100.0*conv*q)/(true_conv*d)+0.5); if d<>100 then begin if font_mag[nf]=pxl_mag_val then begin if not take_defaults then begin if out_mode > errors_only then print_ln(' '); print(' (font as loaded is magnified ',d:1,'%)'); if out_mode=errors_only then print_ln(' '); end @.font as loaded is magnified@> end else begin print_ln(' '); print_ln('Warning!'); print('Font name implies ', font_mag[nf]:5, ' magnification,'); print(' but magnification field shows ',pxl_mag_val:5); @.Warning! Font name implies@> if out_mode=errors_only then print_ln(' '); end; end; incr(nf); {now the new font is officially present} end @* Optional modes of output. \.{DVI2LGP} will print different quantities of information based on some options that the user must specify: The |out_mode| level is set to one of four values (|errors_only|, |terse|, |some_debug|, |verbose|) which were defined back at the start of this \.{WEB} file, giving different degrees of output; and the typeout can be confined to a restricted subset of the pages by specifying the desired starting page and the maximum number of pages. Furthermore there is an option to override the magnification factor that is stated in the \.{DVI} file. In the parent \.{DVItype} program there is an option to specify the resolution of an assumed discrete output device, so that pixel-oriented calculations will be shown, but this is commented out in the present program, since we are thinking exclusively of the \.{LGP}-printer, at 240~pixels/inch. 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, \.{DVI2LGP} will begin with the earliest such page in the file (the interpretation of ``earliest'' depends, of course, on whether the file is being read from front to back or from back to front). The default specification `\.*' (which matches all pages) therefore denotes the page at the beginning of the file. If the file is being read in reverse order so as to get it stacked in normal order on the output tray, then `\.*' matches the last page before the postamble, but if the program is running in what we call |backwards| mode, which is, in fact, reading sequentially from the preamble on down to the postamble, then `\.*' matches the page right after the preamble. When \.{DVI2LGP} begins, it engages the user in a brief dialog so that the options will be specified. This part of \.{DVI2LGP} requires nonstandard \PASCAL\ constructions to handle the online interaction; so it may be preferable in some cases to omit the dialog and simply to stick to the default options (starting page `\.*', |new_mag=0|). On other hand, the system-dependent routines that are needed are not complicated, so it will not be terribly difficult to introduce them. @^system dependencies@> @<Glob...@>= @!out_mode:errors_only..verbose; {controls the amount of output} @!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} @!nett_width:integer; {size of page in pixels, less margins set by printer} @!left_margin:integer; {value in pixels, white space at left edge of paper} @!top_margin:integer; {white space at top} @ The starting page specification is recorded in two arrays called |start_count| and |start_there|. Any one of the values in |start_count| may be declared significant to select the first page for processing, depending on whether the corresponding value in |start_there[]=true| or not. 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. In this example, the remaining values of |start_count| and |start_there| are not important, @<Glob...@>= @!start_count:array[0..9] of integer; {count values to select starting page} @!start_there:array[0..9] of boolean; {is the |start_count| value relevant?} @!start_vals:0..9; {the last count considered significant} @!count:array[0..9] of integer; {the count values on the current page} @ Here are some values for a different and rather cruder set of options. In this case, if |start_on_page>1| the program will skip pages until it gets to the designated |bop|, without considering the values recorded from \TeX's count registers. Finally, there is the |backwards| flag, which determines which way the program will read the pages in the \.{DVI} file. The odd thing about this flag is that sequential reading from $1\to n$ is ``backwards'' because it will deposit the finished pages on the output tray in reverse order. Since most users will prefer to have the pages already arranged in the proper order, the default is to read through from the |postamble| forward, using the backpointers. The switch for changing this default is \.{I} (for ``inverse'' order). Since the default, in this case, requires the use of the |move_to_byte| procedure, we had better admit that it is system dependent. @^system dependencies@> @d sw_c="/" {change this to the switch character you like best} @<Glob...@>= @!start_on_page:integer; {a crude counting method, unlike |start_count|} @!margin_set:integer; {value of |xchr[sw_c]|L, |xchr[sw_c]|C or |xchr[sw_c]|R switch} @!margin_count:integer; {value in pixels} @!right_reading:boolean; {normal vertical output if \.{TRUE}} @!backwards:boolean; {if set to \.{TRUE}, then read from first to last page} @ Here we preset most of the options to the defaults for a normal production run. @d page_default_maximum==1000000 {a million pages is all you are allowed} @d new_mag_default==0 {by default, use the postamble value} @<Set init...@>= out_mode:=errors_only; start_vals:=0; start_there[0]:=false; new_mag:=new_mag_default; max_pages:=page_default_maximum; margin_set:="C"; right_reading:=true; backwards:=false; @^system dependencies@> @ 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. Terminal I/O often involves problems of system dependency, and some systems do not respond at all well fo the use of |write| and |write_ln| for interactive communication with the terminal. The |term_in| file is used for terminal input, and |term_out| for terminal output. @^system dependencies@> @<Glob...@>= @!buffer:array[0..terminal_line_length] of ascii_code; @!term_in:text_file; {the terminal, considered as an input file} @!term_out:text_file; {the terminal, considered as an output file} @ Since the terminal is being used for both input and output, some systems need a special routine to make sure that the user can see a prompt message before waiting for input based on that message. (Otherwise the message may just be sitting in a hidden buffer somewhere, and the user will have no idea what the program is waiting for.) We shall call a system-dependent subroutine |update_terminal| in order to avoid this problem. @^system dependencies@> @d update_terminal == break(term_out) {empty the terminal output buffer} @ During the dialog, \.{DVI2LGP} will treat the first blank space in a line as the end of that line. Therefore |input_ln| makes sure that there is always at least one blank space in |buffer|. @^system dependencies@> @p procedure input_ln; {inputs a line from the terminal} var k:0..terminal_line_length; begin update_terminal; reset(term_in); if eoln(term_in) then read_ln(term_in); k:=0; while (k<terminal_line_length)and not eoln(term_in) do begin buffer[k]:=xord[term_in^]; incr(k); get(term_in); end; buffer[k]:=" "; end; @ The global variable |buf_ptr| is used while scanning each line of input; it points to the first unread character in |buffer|. @<Glob...@>= @!buf_ptr:0..terminal_line_length; {the number of characters read} @ Here is a routine that scans a (possibly signed) integer and computes the decimal value. If no decimal integer starts at |buf_ptr|, the value 0 is returned. The integer should be less than $2^{31}$ in absolute value. @p function get_integer:integer; var x:integer; {accumulates the value} @!negative:boolean; {should the value be negated?} begin if buffer[buf_ptr]="-" then begin negative:=true; incr(buf_ptr); end else negative:=false; x:=0; while (buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9") do begin x:=10*x+buffer[buf_ptr]-"0"; incr(buf_ptr); end; if negative then get_integer:=-x @+ else get_integer:=x; end; @ The selected options are put into global variables by the |dialog| procedure, which is called just as \.{DVI2LGP} begins. @^system dependencies@> @p procedure dialog; label 0,1,2,3,4,5; var k:integer; {loop variable} ctr, tctr, lctr, dctr: integer; {loop variables for getting file names} begin rewrite(term_out); {prepare the terminal for output} if argc < 2 then @t{\kern1em}}%--fixme--@>@/ {presently assumes---if more than one argument, use default} print_ln(banner);@/ @<Get \.{DVI} and \.{LGP} file names@>; @<Set option switch values@>; @<Determine the desired |out_mode|@>; {from \.{DVItype}} @<Determine the desired |start_count| values@>; @<Determine the desired |max_pages|@>; @<Determine the desired |new_mag|@>; @<Determine the desired |resolution|@>; {from \.{DVItype}} if not take_defaults then begin @<Print all the selected options@> end; end; @ Here are some global flags which are used to direct the processing. |first_get| is defined because it is necessary to flag the first |get| done so that an initial |eoln| is accepted without requiring another character be entered. |take_defaults| is defined to distinguish the case of file name entered on command line from the case when the file name was queried for. @<Glob...@>= @!first_get:boolean; {true at start, then always false} @!take_defaults:boolean; {true if defaults should be taken without prompting} @!interaction:integer; {level of interaction, perhaps only batch and not batch} @^system dependencies@> @ @<Set init...@>= first_get:=true; {needed in some cases, harmless in others} interaction:=batch_mode+1; {that takes care of that for now} @ It is necessary for both |open_dvi_file| and |open_lgp_file| to know what the \.{DVI} file name is. Hence, |dvi_name| is declared global. |lgp_name| is then generated from the essential part of the |dvi_name|. This operation assumes that we have access to the same extensions of the standard |reset| statement as are used for |pxl_files| or |vrl_files|. |argv_nm_type| is defined here because the TOPS-20 Pascal compiler does not allow the definition |packed array[1..name_length] of text_char| in the |argv| procedure declaration which we will be supplying later. The {\mc UNIX} |argv| also returns a |packed array [1..name_length] of char|. @<Types...@>= @!argv_nm_type = packed array[1..name_length] of text_char; @ @<Glob...@>= @!dvi_name : argv_nm_type; {the name of the \.{DVI} file either on the command line or entered from the terminal--returned from the |argv| procedure} @!lgp_name: packed array [1..name_length] of char; {the name of the \.{LGP} file} @^system dependencies@> @ Here we want to generate the file names for the |dvi_file| and the |lgp_file|. If the |dvi_file| name is not present on the command line, it is prompted for. The |lgp_name| is generated from |dvi_name|. This section makes reference to two non-standard procedures which are part of the {\mc UNIX} \PASCAL\ environment, but which must be provided in the system dependent code in other environments such as TOPS-20. The |argc| function shows the number of arguments on the command line, and must return a value |>=1| unless the system has gone completely berserk. The |argv| procedure extracts any argument from the command line (with the value 0 in the first argument for the first element of the command line). @<Get \.{DVI} and \.{LGP} file names@>= if argc > 2 then begin write_ln(term_out, 'Usage: dvi2lgp dvifile[.dvi]'); jump_out;@t}%--fixme--@>@/ end else if argc <> 2 then begin @<Ask for the \.{DVI} file name@>@; take_defaults:=false; end else begin argv(1, dvi_name); take_defaults:=true; end; @<Make sure \.{DVI} file has extension@>@; @<Show the \.{DVI} file name@>@; @<Figure out what to name the \.{LGP} file@>@; @<Print the \.{LGP} file name@>@; @^system dependencies@> @ The \.{DVI} file name may be specified in one of two ways. In the first, it is included on the command line which invokes \.{DVI2LGP}. In the second, used when no names were specified on the command line, \.{DVI2LGP} asks for the names. Here is where we define the code to ask for the name. @<Ask for the \.{DVI} file name@>= tprint('> DVI file name? '); input_ln; while((buffer[0] = " ") or (buffer[0] = "?")) do begin if(buffer[0] = " ") then tprint('Whoops, please enter a non-null name? ') else begin tprint_ln('Enter the name of the DVI file produced by TeX. You can'); tprint_ln('leave the extension off if you wish.'); tprint('> DVI file name? '); end; input_ln; end; ctr := 0; while (buffer[ctr] <> " ") do begin if (buffer[ctr]>="a") and (buffer[ctr]<="z") then dvi_name[ctr+1]:=system_case_of(buffer[ctr]) {harmless do-nothing in {\mc UNIX}} else dvi_name[ctr+1] := xchr[buffer[ctr]]; incr(ctr); end; incr(ctr); { set remainder of buffer to blanks } while(ctr <= name_length) do begin dvi_name[ctr] := ' '; incr(ctr); end; @^system dependencies@> @ Scan through the \.{DVI} file name which has been extracted to guarantee that it has an extension. If none, add a \.{.DVI} to the end. (Always assuming that your system uses this sort of suffix.) The |alt_marker| can be the same as the |area_marker| in a {\mc UNIX} system, or it can be defined as the closing bracket for an alternate directory. @d area_marker==':' {or whatever your system uses} @d alt_marker=='>' {change this to things like the $\rangle$ directory bracket} @d ext_marker=='.' {the normal character for file-name extensions} @<Make sure \.{DVI} file has extension@>= ctr:=1; while(dvi_name[ctr]<>' ') do incr(ctr); { find the end of the name } tctr:=ctr-1; while((dvi_name[tctr]<>area_marker) and (dvi_name[tctr]<>alt_marker) and (dvi_name[tctr]<>ext_marker) and (tctr>1)) do decr(tctr); if(dvi_name[tctr]<>ext_marker) then begin { no extension, add one } dvi_name[ctr] := ext_marker; incr(ctr); dvi_name[ctr] := system_case_of("d"); incr(ctr); dvi_name[ctr] := system_case_of("v"); incr(ctr); dvi_name[ctr] := system_case_of("i"); incr(ctr); dvi_name[ctr] := ' '; end; @^system dependencies@> @ Having determined what the \.{DVI} file name is, we now want to display it. @<Show the \.{DVI} file name@>= if interaction > batch_mode then begin print('['); ctr := 1; while(dvi_name[ctr] <> ' ') do begin print(dvi_name[ctr]); incr(ctr); end; print('->'); end; @^system dependencies@> @ Given the |dvi_name|, we now need to figure out where to put the \.{LGP} file. We will put the \.{LGP} file into a |lgp_name| which is in the local directory, with the same file name as |dvi_name|, but with the extension \.{.LGP} rather than \.{.DVI}. @<Figure out what to name the \.{LGP} file@>= dctr := 1; {for traversing the \.{DVI} file name} {find the end of the \.{DVI} file name} while(dvi_name[dctr] <> ' ') do incr(dctr); {back up until either |area_marker|, |alt_marker| or beginning of name encountered} while((dvi_name[dctr] <> area_marker) and (dvi_name[dctr]<> alt_marker) and (dctr > 1)) do decr(dctr); {unles we are right at the beginning, skip this token} if((dvi_name[dctr]=area_marker) or (dvi_name[dctr]=alt_marker)) then incr(dctr); {copy the \.{DVI} name to the \.{LGP} file name array} lctr := 1; {for traversing the \.{LGP} file name} while((dvi_name[dctr] <> ext_marker) and (dvi_name[dctr] <> ' ')) do begin lgp_name[lctr] := dvi_name[dctr]; incr(lctr); incr(dctr); end; lgp_name[lctr] := ext_marker; incr(lctr); lgp_name[lctr] := system_case_of("l"); incr(lctr); lgp_name[lctr] := system_case_of("g"); incr(lctr); lgp_name[lctr] := system_case_of("p"); incr(lctr); lgp_name[lctr] := ' '; {and blank out the rest of the \.{LGP} name array} for ctr:=lctr+1 to name_length do lgp_name[ctr] := ' '; @^system dependencies@> @ And now finish the line which was started back there with the \.{DVI} filename print-out by printing the \.{LGP} file name. @<Print the \.{LGP} file name@>= if interaction > batch_mode then begin ctr := 1; while(lgp_name[ctr] <> ' ') do begin print(lgp_name[ctr]); incr(ctr); end; print_ln(']'); end @^system dependencies@> @ The option switches are an addition to the \.{DVItype} code. @<Set option...@>= if take_defaults then goto 5; 0: tprint('> <CR> to accept default of all pages, '); tprint_ln('postamble-specified magnification.'); tprint(' ? for help: '); @.CR to accept default...@> input_ln; buf_ptr:=0; if buffer[buf_ptr]=" " then goto 5 else if buffer[buf_ptr]="?" then begin tprint_ln('Type any character (which will be ignored), except for "?", if you'); tprint_ln('want to specify values. Type CR if you are willing to accept the'); tprint_ln('default values. Typing "?" prints this message. Typing "?" in'); tprint_ln('response to any of the following questions also produces help text.'); goto 0; end; 1: tprint('> Switches, in form ',xchr[sw_c],'switch',xchr[sw_c],'etc. (No spaces): '); @.Switches, in form...@> input_ln; buf_ptr:=0; start_on_page:=0; max_pages:=page_default_maximum; {preset to default value} if buffer[buf_ptr]=sw_c then begin while buffer[buf_ptr]<>" " do if buffer[buf_ptr]=sw_c then begin incr(buf_ptr); if (buffer[buf_ptr]>="0") and (buffer[buf_ptr]<="9") then begin start_on_page:=get_integer; max_pages:=1; backwards:=true; if buffer[buf_ptr]=":" then begin incr(buf_ptr); if (buffer[buf_ptr]="E") or (buffer[buf_ptr]="e") then max_pages:=page_default_maximum else max_pages:=(get_integer-start_on_page)+1; if max_pages<1 then begin max_pages:=page_default_maximum; tprint_ln('Negative or zero page count reset to default of 1000000!'); @.Negative or zero page count@> end; end; end else begin if (buffer[buf_ptr]>="a") or (buffer[buf_ptr]<="z") then buffer[buf_ptr]:=xord[fold_upper(buffer[buf_ptr])]; {fold into uppercase, not an optional fold} @<Set the switches@> end; while(buffer[buf_ptr]<>sw_c)and(buffer[buf_ptr]<>" ")do incr(buf_ptr); end; end else if buffer[buf_ptr]<>" " then begin tprint_ln('The following switches are available: '); @.The following switches@> tprint_ln(xchr[sw_c],'L(eft)',xchr[sw_c],'C(enter)',xchr[sw_c], 'R(ight) for position on page,'); tprint_ln(xchr[sw_c],'n or ',xchr[sw_c],'n:m or ',xchr[sw_c], 'n:E(nd) for simple page count,'); tprint(xchr[sw_c],'P(roduction)',xchr[sw_c],'T(erse)',xchr[sw_c], 'S(ome-debug)',xchr[sw_c],'F(ull-debug)'); tprint_ln(' for mode selection.'); tprint_ln (xchr[sw_c],'I(nvert) for output in DVI-file order--inverse order on tray'); tprint_ln (xchr[sw_c],'H(orizontal) or V(ertical)--Vertical is the default'); @.mode selection@> @.order on tray@> @.H(orizontal) or V(ertical)@> goto 1; end; @^system dependencies@> @ @<Set the switches@>= case buffer[buf_ptr] of "C": begin margin_set:="C";@+ end; {this is the default} "F": begin out_mode:=verbose; backwards:=true; end; "H": begin right_reading:=false; end; "I": begin backwards:=true; end; "L": begin margin_set:="L"; end; "R": begin margin_set:="R"; end; "P": begin out_mode:=errors_only;@+ end; {this is the default} "T": begin out_mode:=terse; end; "S": begin out_mode:=some_debug; backwards:=true; end; "V": begin right_reading:=true;@+ end; {this is the default} othercases begin print_ln('Undefined switch: ''',xchr[buffer[buf_ptr]],''', try again'); goto 1; @.Undefined switch@> end endcases @ @<Determine the desired |out_mode|@>= do_nothing {we already took care of this with \.{P, T, S,} or \.{F}} @ @<Determine the desired |start...@>= 2: tprint('> Starting page (default='); if start_on_page>0 then begin tprint_ln(start_on_page:1,', simple count, set by ',xchr[sw_c],'n switch)'); end else tprint_ln('*) '); tprint(' (for help, type ?): '); start_vals:=0; start_there[0]:=false; input_ln; buf_ptr:=0; k:=0; if (buffer[0]<>" ") and (start_on_page<>0) then begin if buffer[0]<>"?" then tprint_ln(' (TeX''s \count values override the ',xchr[sw_c],'n value.)') end; if buffer[0]<>" " then repeat if buffer[buf_ptr]="*" then begin start_there[k]:=false; incr(buf_ptr); start_on_page:=0; max_pages:=page_default_maximum;{cancel any /n values} end else begin if (buffer[0]="-")or((buffer[buf_ptr]>="0") and (buffer[buf_ptr]<="9")) then begin start_on_page:=0; max_pages:=page_default_maximum;{cancel any /n values} end; start_there[k]:=true; start_count[k]:=get_integer; backwards:=true; 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 tprint('At this point you can make use of the values '); tprint_ln('in TeX''s \count registers '); tprint('Type, e.g., 1.*.-5 to specify the '); tprint_ln('first page with \count0=1, \count2=-5.'); tprint_ln('When you specify a *, dvi2lgp will just print out the corresponding'); tprint_ln('\count field but won''t pay attention to it.'); tprint_ln('If you type *.*.*.*.*.*.*.*.*.*, you will just see what was'); tprint_ln('in all of the \count fields when TeX output the page.'); if start_on_page<>0 then begin tprint_ln('(If you enter anything but ? here, you will use TeX''s \count'); @.If you use TeX's count@> tprint_ln('values which will override the ',xchr[sw_c],'n value.)'); end; goto 2; end; until start_vals=k @ @<Determine the desired |max_pages|@>= 3: tprint('> Maximum number of pages (default=',max_pages:1); if max_pages <> page_default_maximum then tprint(' as set by use of the ',xchr[sw_c],'n switch'); {this situation results from use of the |sw_c|n or |sw_c|n:m switch} tprint('): '); input_ln; buf_ptr:=0; if buffer[0]="?" then begin tprint_ln('Maximum number of pages to be printed, a positive number'); goto 3; end; if buffer[0]<>" " then begin max_pages:=get_integer; end; if max_pages<=0 then begin tprint_ln('The maximum number of pages must be positive.'); max_pages:=page_default_maximum; goto 3; end @ @<Determine the desired |new_mag|@>= 4: tprint_ln('> New magnification (default is the magnification value'); tprint(' in the DVI postamble): '); new_mag:=new_mag_default; input_ln; buf_ptr:=0; if buffer[0]<>" " then begin if buffer[0]="m" then @<Check for magstep@> else begin if (buffer[0]>="0")and(buffer[0]<="9") then new_mag:=get_integer else begin tprint_ln('Type a 0 to retain the value specified in the DVI postamble.'); tprint('Type a positive integer '); tprint_ln('or specify a magstep to override '); tprint_ln('the magnification in the DVI file.'); tprint_ln('You may either specify a magstep (e.g. magstep1), or'); tprint('type an integer equal to 1000 times desired magnification'); tprint_ln(' (e.g. 1315)'); goto 4; end end end @ @<Determine the desired |resolution|@>= 5: resolution:=240.0 {This value is fixed for the \.{LGP}} @ @<Check for magstep@>= begin if ((buffer[1]="a") and (buffer[2]="g") and (buffer[3]="s") and (buffer[4]="t") and (buffer[5]="e") and (buffer[6]="p")) {whew!} then case buffer[7] of "h": begin if ((buffer[8]="a") and(buffer[9]="l") and(buffer[10]="f")) then new_mag:=1095 else begin tprint_ln('Not a valid magstep specification'); goto 4; end end; "0": begin new_mag:=1000; tprint_ln(' '); tprint_ln ('That''s no magnification at all!'); end; "1": begin new_mag:=1200; end; "2": begin new_mag:=1440; end; "3": begin new_mag:=1728; end; "4": begin new_mag:=2074; tprint_ln(' '); tprint_ln ('That''s ABSOLUTELY HUGE! Will it fit, and do the fonts exist?'); tprint_ln(' '); end; "5","6","7","8","9": begin for k:=0 to 7 do tprint(xchr[buffer[k]]); tprint_ln(' is MUCH TOO LARGE!'); tprint_ln(' '); goto 4; end; othercases begin tprint_ln(' '); tprint_ln ('Not a valid magstep specification'); goto 4; end; endcases end @ After the dialog is over, we print the options so that the user can see what \.{DVI2LGP} thought was specified. @<Print all the selected options@>= print_ln('Options selected:'); @.Options selected@> if backwards then print_ln('Read DVI from front to back, inverted output') else print_ln('Read DVI from back to front, output on tray in order'); if not right_reading then print_ln('Rotated (landscape mode) output'); print(' Starting page = '); if start_on_page = 0 then begin for k:=0 to start_vals do begin if start_there[k] then print(start_count[k]:1) else print('*'); if k<start_vals then print('.') else print_ln(' '); end; end else print_ln(start_on_page:1,' (sequential count, not TeX''s \count)'); print_ln(' Maximum number of pages = ',max_pages:1); print(' Output level = ',out_mode:1); case out_mode of errors_only: print_ln(' (bops, error messages and LGP-code file)'); terse: print_ln(' (bops, fonts, error messages and LGP-code file)'); some_debug: print_ln(' (line-by-line debug, with LGP-code file)'); verbose: print_ln(' (byte-by-byte debug, with LGP-code file)'); end;@/ print_ln(' Resolution = ',resolution:12:8,' pixels per inch'); if new_mag>0 then print_ln(' New magnification factor = ',get_actual_mag(new_mag):8:3) @^system dependencies@> @* Low level output routines. Simple text in the \.{DVI} file is saved in a buffer until |line_length-2| characters have accumulated, or until some non-simple \.{DVI} operation occurs. Then the accumulated text is printed on a line, surrounded by brackets. The global variable |text_ptr| keeps track of the number of characters currently in the buffer. @<Glob...@>= @!text_ptr:0..line_length; {the number of characters in |text_buf|} @!text_buf:array[1..line_length] of ascii_code; {saved characters} @ @<Set init...@>= text_ptr:=0; @ The |flush_text| procedure will empty the buffer if there is something in it. @p procedure flush_text; var k:0..line_length; {index into |text_buf|} begin if text_ptr>0 then begin if out_mode>terse then begin print('['); for k:=1 to text_ptr do print(xchr[text_buf[k]]); print_ln(']'); end; text_ptr:=0; end; end; @ And the |out_text| procedure puts something in it. @p procedure out_text(c:ascii_code); begin if text_ptr=line_length-2 then flush_text; incr(text_ptr); text_buf[text_ptr]:=c; end; @* Native-mode commands for the {\tentex LGP}. The \.{LGP} is driven by escape code sequences, using the {\mc ASCII} escape (decimal 27) as the initial character. A full list of these is provided in the \\{Symbolics, Inc.} technical manual for the \.{LGP}. The ones we make use of here will largely be understood by reference to the code used. Following the escape, we may have the \\{character}, which is a code in the range of the {\mc ASCII} table (the same range as |ascii_code|), the \\{unsigned byte}, with a value of |0..255|, the \\{signed byte}, with a value of |-128..127|, and the signed 16-bit microprocessor \\{``word''} with a value of |-32768..32767|. The |ascii_code| value following the escape specifies the desired action, and is reasonably evocative of what we intend. \yskip \item{\.{"b"}} paints pixels directly on the page bit-map, and does not use up any of the \.{LGP}'s font storage area. See under ``Define a Block of Raster Data'' in the index. \item{\.{"c"}} defines a character with fairly hefty dimensions. See under ``Define a Large-Size Character'' in the index. \item{\.{"d"}} defines a character. See under ``Define a Small-Size Character'' in the index. \item{\.{"e"}} end the job---a simple two character sequence without parameters. \item{\.{"f}"} select a font. See under ``Set Font'' in the index. \item{\.{"m"}} move around on the bit-map. See the second entry under ``Define a a Small-Size Character,'' and also under ``Draw a Rectangle.'' \item{\.{"o"}} define a forms overlay. See under ``Specials'' in the index. \item{\.{"p"}} finish a page---no parameters. Tells the \.{LGP} to go ahead and print this page. \item{\.{"r"}} set a rule. See the index under ``Draw a Rectangle.'' \item{\.{"v"}} draw a vector. See under ``Specials'' in the index. @ We begin by setting up a macro for the initial escape-code string, another for the familiar ``low-high'' sequence of bytes so beloved by microprocessors, and a third which is used to send a pair of \\{escape} codes if we want to set a character with the same |ascii_code| value as \\{escape}. @d escape_string(#)==begin output_byte(27); output_byte(#) end @d send_in_low_high_sequence(#)== begin output_byte(# mod 256); output_byte(# div 256) end @d output_char(#)==begin if #=27 then output_byte(27); output_byte(#) end @ The printer's microprocessor program has one further idiosyncracy, in that it wants each byte of the bit-maps for characters with the bits in reversed order. It would be excessively tedious to do the calculations for this reversal each time it is needed, so we do it once for all in the initialization by setting up an array representing the values $0\to 255$ with the bits in reverse order---high-order bit to the right. @<Glob...@>= @!bits_reversed:array[0..255] of 0..255; @ @<Set init...@>= for m:=0 to 255 do begin l:=m; n:=0; i:=1; j:=128; for k:=1 to 8 do begin if (l div j)<>0 then begin n:=n+i; l:=l-j end; i:=2*i; j:=j div 2 end; bits_reversed[m] := n; end; \f @* Translation to {\tentex LGP}-code or to symbolic form. The main work of \.{DVI2LGP} 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 stack of $(h,v,w,x,y,z)$ values is represented by eight arrays called |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|. @<Glob...@>= @!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values} @!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack: array [0..stack_size] of integer; {pushed down values in \.{DVI} units} @!hhstack,@!vvstack: array [0..stack_size] of integer; {pushed down values in pixels} @!lgp_h,@!lgp_v:integer; {where we got to after the last |set_char|, \\{vel sim}} @ 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; \.{DVI2LGP} checks to see if this total is accurate. @d infinity==@'17777777777 {$\infty$ (approximately) for range checks etc.} @<Glob...@>= @!max_v:integer; {the value of |abs(v)| should probably not exceed this} @!max_h:integer; {the value of |abs(h)| should probably not exceed this} @!max_s:integer; {the stack depth should not exceed this} @!max_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} @!hh_vector,@!vv_vector:integer; {used for the \.{LGP} vector drawing feature} @ @<Set init...@>= max_v:=infinity; max_h:=infinity; max_s:=stack_size+1;@/ max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0; hh_vector:=infinity;vv_vector:=infinity; @ 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<conv*x then rule_pixels:=n+1 @+ else rule_pixels:=n; end; @ Strictly speaking, the |do_page| procedure is really a function with side effects, not a `\&{procedure}'; it returns the value |false| if \.{DVI2LGP} should be aborted because of some unusual happening. The subroutine is organized as a typical interpreter, with a multiway branch on the command code followed by |goto| statements leading to routines that finish up the activities common to different commands. We will use the following labels: @d fin_set=41 {label for commands that set or put a character} @d fin_rule=42 {label for commands that set or put a rule} @d move_right=43 {label for commands that change |h|} @d move_down=44 {label for commands that change |v|} @d show_state=45 {label for commands that change |s|} @d change_font=46 {label for commands that change |cur_font|} @ Some \PASCAL\ compilers severely restrict the length of procedure bodies, so we shall split |do_page| into two parts, one of which is called |special_cases|. The different parts communicate with each other via the global variables mentioned above, together with the following ones: @<Glob...@>= @!s:integer; {current stack size} @!ss:integer; {stack size to print} @!cur_font:integer; {current internal font number} @!showing:boolean; {is the current command being translated in full?} @ Here is the overall setup. @p @<Declare the function called |special_cases|@>@; function do_page:boolean; label fin_set,fin_rule,move_right,show_state,done,9998,9999; var o:eight_bits; {operation code of the current command} @!p,@!q:integer; {parameters of the current command} @!a:integer; {byte number of the current command} @!i,@!j:integer; {to hold temporaries for |escape_string("m")| command etc.} @!hs,@!vs: integer; {used to define character for \.{LGP} memory} @!hs_in_pixels,hs_in_bytes: integer; {to make the job more exact} @!lgp_sixteen_bit_words,lgpsbw_count:integer; {used to trim down wasted font storage in the \.{LGP}} @!bit_columns,@!bit_rows:integer; {size of envelope in bits} @!glyph_bytes: array [0..10000] of integer; {temporary storage for bit-map} @!glyph_step,@!row_offset:integer; {offset start of index and govern size of increment through array} @!clump_columns,@!clump_limit,non_pixels:integer; {control movement back through unrotated row} @!rotated_byte:integer; {what we are trying to achieve} begin cur_font:=nf; {set current font undefined} s:=0; h:=0; v:=0; w:=0; x:=0; y:=0; z:=0; hh:=left_margin; if right_reading then vv:=page_pixel_height - top_margin else vv:=top_margin; {initialize the state variables} escape_string("m"); send_in_low_high_sequence(0); send_in_low_high_sequence(0); while true do @<Translate the next command in the \.{DVI} file; |goto 9999| with |do_page=true| if it was |eop|; |goto 9998| if premature termination is needed@>; 9998: print_ln('!'); do_page:=false; 9999: end; @ Commands are broken down into ``major'' and ``minor'' categories: A major command is always shown in full, while a minor one is put into the buffer in abbreviated form. Minor commands, which account for the bulk of most \.{DVI} files, involve horizontal spacing and the typesetting of characters in a line; these are shown in full only if |out_mode>=verbose|. @d show(#)==begin flush_text; showing:=true; print(a:1,': ',#); end @d major(#)==if out_mode>terse then show(#) @d minor(#)==if out_mode=verbose then begin showing:=true; print(a:1,': ',#); end @d error(#)==if not showing then show(#) else print(' ',#) @<Translate the next command...@>= begin a:=cur_loc; showing:=false; o:=get_byte; p:=first_par(o); if eof(dvi_file) then bad_dvi('the file ended prematurely!'); @.the file ended prematurely@> @<Start translation of command |o| and |goto| the appropriate label to finish the job@>; fin_set: @<Finish a command that either sets or puts a character, then |goto move_right| or |done|@>; fin_rule: @<Finish a command that either sets or puts a rule, then |goto move_right| or |done|@>; move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>; show_state: @<Show the values of |ss|, |h|, |v|, |w|, |x|, |y|, |z|, |hh|, and |vv|; then |goto done|@>; done: if showing then print_ln(' '); end @ The multiway switch in |first_par|, above, was organized by the length of each command; the one in |do_page| is organized by the semantics. @<Start translation...@>= if o<set_char_0+128 then @<Translate a |set_char| command@> else case o of four_cases(set1): begin major('set',o-set1+1:1,' ',p:1); goto fin_set; end; set_rule: begin major('setrule'); goto fin_rule; end; put_rule: begin major('putrule'); goto fin_rule; end; @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@; @t\4@>@<Cases for horizontal motion@>@; othercases if special_cases(o,p,a) then goto done@+else goto 9998 endcases @ @<Declare the function called |special_cases|@>= function special_cases(@!o:eight_bits;@!p,@!a:integer):boolean; label change_font,move_down,done,9998; var q:integer; {parameter of the current command} @!k,n:integer; {loop indices, the |n| is used for \.{\\special}s} @!spec_string:array[1..name_length] of ascii_code; @!bad_char:boolean; {has a non-ascii character code appeared in this |xxx|?} @!pure:boolean; {is the command error-free?} @!end_of_special:boolean; {end of overlay or big character found} begin pure:=true; case o of four_cases(put1): begin major('put',o-put1+1:1,' ',p:1); goto done; end; @t\4@>@<Cases for vertical motion@>@; @t\4@>@<Cases for fonts@>@; four_cases(xxx1): @<Translate an |xxx| command and |goto done|@>; pre: begin error('preamble command within a page!'); goto 9998; end; @.preamble command within a page@> post,post_post: begin error('postamble command within a page!'); goto 9998; @.postamble command within a page@> end; othercases begin error('undefined command ',o:1,'!'); goto done; @.undefined command@> end endcases; move_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>; change_font: @<Finish a command that changes the current font, then |goto done|@>; 9998: pure:=false; done: special_cases:=pure; end; @ @<Cases for commands |nop|, |bop|, \dots, |pop|@>= nop: begin minor('nop'); goto done; end; bop: begin error('bop occurred before eop'); goto 9998; @.bop occurred before eop@> end; eop: begin major('eop'); escape_string("p"); if s<>0 then error('stack not empty at end of page (level ', s:1,')!'); @.stack not empty...@> if (hh_vector<>infinity) or (vv_vector<>infinity) then error('page ended with an unused start-of-vector: hh= ', hh_vector:1, ', vv= ', vv_vector:1, '!'); hh_vector:=infinity;vv_vector:=infinity; do_page:=true; if out_mode > terse then print_ln(' '); goto 9999; end; push: begin major('push'); 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 begin error('DVI2LGP capacity exceeded (stack size=', stack_size:1,')'); goto 9998; end; end; hstack[s]:=h; vstack[s]:=v; wstack[s]:=w; xstack[s]:=x; ystack[s]:=y; zstack[s]:=z; hhstack[s]:=hh; vvstack[s]:=vv; incr(s); ss:=s-1; goto show_state; end; pop: begin major('pop'); 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 show_state; 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. @d pixel_round(#)==trunc(conv * (#)+0.5) @d out_space(#)==if abs(p)>=font_space[cur_font] then begin if out_mode>terse then out_text(" "); hh:=pixel_round(h+p)+left_margin; end else hh:=hh+pixel_round(p); minor(#,' ',p:1); q:=p; goto move_right @<Cases for horizontal motion@>= four_cases(right1):begin out_space('right',o-right1+1:1); end; w0,four_cases(w1):begin w:=p; out_space('w',o-w0:1); end; x0,four_cases(x1):begin x:=p; out_space('x',o-x0:1); end; @ Vertical motion is done similarly, but with the threshold between ``small'' and ``large'' increased by a factor of five. The idea is to make fractions like ``$1\over2$'' round consistently, but to absorb accumulated rounding errors in the baseline-skip moves. @d out_vmove(#)==if abs(p)>=5*font_space[cur_font] then begin if right_reading then vv:=page_pixel_height-(top_margin+(pixel_round(v+p))) else vv:=top_margin+(pixel_round(v+p)); end else begin if right_reading then vv:=vv-pixel_round(p) { in \.{DVItype} you add---here you subtract } else vv:=vv+pixel_round(p) {but not in rotated mode} end; major(#,' ',p:1); goto move_down @<Cases for vertical motion@>= four_cases(down1):begin out_vmove('down',o-down1+1:1); end; y0,four_cases(y1):begin y:=p; out_vmove('y',o-y0:1); end; z0,four_cases(z1):begin z:=p; out_vmove('z',o-z0:1); end; @ @<Cases for fonts@>= sixty_four_cases(fnt_num_0): begin major('fntnum',p:1); goto change_font; end; four_cases(fnt1): begin major('fnt',o-fnt1+1:1,' ',p:1); goto change_font; end; four_cases(fnt_def1): begin major('fntdef',o-fnt_def1+1:1,' ',p:1); define_font(p); goto done; end; @ Here is where we add some special \.{LGP} features, which will be called up by the use of the \.{\\special} primitive in \TeX. See the chapter, ``\&{Special }{\tentex LGP}\&{ functions}'' for more information. @<Translate an |xxx| command and |goto done|@>= begin major('xxx'''); bad_char:=false; for k:=1 to name_length do spec_string[k]:=0; for k:=1 to p do begin q:=get_byte; spec_string[k]:=q; if (q>=" ")and(q<="~") then begin if showing then print(xchr[q]); end else bad_char:=true; end; if showing then print(''''); if bad_char then error('non-ascii character in xxx command!'); if ((spec_string[1]="b") or (spec_string[1]="v") or (spec_string[1]="o") or (spec_string[1]="r") or (spec_string[1]="s")) then @<Check for valid \.{LGP} special@>; @.non-ascii character...@> goto done; end @^Specials@> @ @<Translate a |set_char|...@>= begin @<Check for correct position on full-page bit-map, and update if needed@>; with font_mem[cur_font,p] do begin q:= tfm_width; {used later, for left to right movement} if raster_ptr<>0 then begin if raster_ptr=-1 then output_char(p) {character already in \.{LGP} memory} else begin active_font:=open_font(cur_font); hs_in_pixels:=(envelope div half_shift); hs:=(hs_in_pixels+31) div 32; {number of 32-bit words per line} hs_in_bytes:=(hs_in_pixels+7) div 8; lgp_sixteen_bit_words:=(hs_in_pixels+15) div 16; vs:=(envelope mod half_shift); if (hs*vs) < big_char_threshold then @<Define a small-size character@> else @<Use 16-bit arguments for character dimensions@>; pxl_move_to_byte(raster_ptr); @<Send the rasters@>; if (hs*vs) < huge_char_threshold then begin raster_ptr:=-1; {if {\tt "d"} or {\tt "c"} string used, mark this character as stored} output_char(p); {and call it into the text stream for setting} end end; @^Define a Small-Size Character@> @^Define a Large-Size Character@> @^Define a Block of Raster Data@> end else begin print_font(cur_font); print_ln(': character ',p:1,' is undefined') end; end; if (o>" ")and(o<="~") then begin if out_mode>terse then out_text(p); minor('setchar',p:1); end else major('setchar',p:1); goto fin_set; end @ For most characters we can use byte-length arguments for all dimensions. @<Define a small-size...@>= begin escape_string("d"); {use byte-length arguments for character dimensions} output_byte(p); output_byte(cur_font); if right_reading then begin output_byte(lgp_sixteen_bit_words); output_byte(vs); output_byte(-x_offset); output_byte(y_offset); output_byte(lgp_width); {set \\{xinc} value to advance the print position in the \.{LGP}} output_byte(0); {zero for the \\{yinc} value} end else begin output_byte((vs+15)div 16); {16 bit micro-processor words/scan line} output_byte(hs_in_pixels); output_byte(-y_offset); output_byte(hs_in_pixels - x_offset); output_byte(0); {zero for the \\{xinc} value} output_byte(lgp_width); {set \\{yinc} value to advance the print position in the \.{LGP}} end end @ For oversize characters we use either the ``Define a Large-Size Character'' sequence (with 16-bit arguments), or the ``Define a Block of Raster Data'' sequence that paints directly on the bit map, and does not use up storage in the \.{LGP}. @d sign_extension = @'377 @<Use 16-bit...@>= begin if (hs*vs) < huge_char_threshold then begin escape_string("c"); output_byte(p); output_byte(cur_font); end else escape_string("b"); if right_reading then begin send_in_low_high_sequence(lgp_sixteen_bit_words); {16 bit micro-processor words per scan line} send_in_low_high_sequence(vs); output_byte(-(x_offset mod 256)); if x_offset>255 then output_byte(-(x_offset div 256)) else begin if x_offset>0 then output_byte(sign_extension) else output_byte(-(x_offset) div 256); end; {These tiresome complications necessary for negative values} if y_offset>=0 then send_in_low_high_sequence(y_offset) else begin i:=-y_offset; output_byte(-(i mod 256)); if i>255 then output_byte(-(i div 256)) else output_byte(sign_extension); end; {These tiresome complications necessary for negative values} send_in_low_high_sequence(lgp_width); {set \\{xinc} value to advance the print position in the \.{LGP}} output_byte(0); output_byte(0); {zero for the \\{yinc} value} end else begin i:=((vs+15)div 16); {calculate first, then send} send_in_low_high_sequence(i); {16 bit micro-processor words per scan line} send_in_low_high_sequence(hs_in_pixels); output_byte(-(y_offset mod 256)); if y_offset>255 then output_byte(-(y_offset div 256)) else begin if y_offset>0 then output_byte(sign_extension) else output_byte(-(y_offset) div 256); end; {These tiresome complications necessary for negative values} i:=(hs_in_pixels - x_offset); {calculate first, then send} if i>=0 then send_in_low_high_sequence(i) else begin i:=-i; output_byte(-(i mod 256)); if i>255 then output_byte(-(i div 256)) else output_byte(sign_extension); end; {These tiresome complications necessary for negative values} output_byte(0); output_byte(0); {zero for the \\{xinc} value} send_in_low_high_sequence(lgp_width); {set \\{yinc} value to advance the print position in the \.{LGP}} end end @^Define a Block of Raster Data@> @^Define a Large-Size Character@> @ If the current \.{DVI} |set_char| command was preceded by any of the various \.{DVI} commands which alter the |h| and |v| coordinates, it is necessary to change coordinates in the \.{LGP}. This action is unnecessary after a preceding |set_char|, |fnt_num| or |fnt_def|, if we discover that the baseline has not changed, since we can assume that the horizontal position has been updated in the \.{LGP} by adding the \\{xinc} value (or in the case of rotated characters, the \\{yinc} value) from the font character definition of the last character processed to the previous position. (Right-reading font character definitions in this program do not use the \\{yinc} value, which is always set to 0. In rotated definitions, it is the \\{xinc} value which is set to 0.) @<Check for correct...@>= begin if (lgp_h<>h) or (lgp_v<>v) then begin {see whether either has been changed} escape_string("m"); if right_reading then begin send_in_low_high_sequence(hh); send_in_low_high_sequence(vv); end else begin send_in_low_high_sequence(vv); send_in_low_high_sequence(hh); end end; end @^Define a Small-Size Character@> @ @<Send the rasters@>= begin lgpsbw_count:=lgp_sixteen_bit_words; if right_reading then begin for k:=1 to hs*vs do begin read_pxl_bytes; output_byte(bits_reversed[b0]); output_byte(bits_reversed[b1]); decr(lgpsbw_count); if lgpsbw_count>0 then begin {this trims out unnecessary bytes of zero} output_byte(bits_reversed[b2]); output_byte(bits_reversed[b3]); decr(lgpsbw_count); end; if lgpsbw_count=0 then lgpsbw_count:=lgp_sixteen_bit_words; end end else begin @<Copy |glyph_bytes| into temporary array@>; bit_columns:=hs_in_pixels; {full bit count} glyph_step:=hs*4; {controls increment of index; 4 bytes/32-bit loose-packed \.{PXL} word} row_offset:=hs_in_bytes-1; {controls starting point} @<Read bit-columns from right to left, to get past zero-fill, shifting as you go@>; clump_columns:=0; repeat @<Pick up column bits, top to bottom, starting with right-hand column@> until bit_columns = 0; end end @ This routine ticked along just fine until the arrival of the first font with zero width picture drawing characters. Then we discovered that there has to be a special provision to force the \.{LGP} update its coordinates when there is a sequence of zero-width characters. @<Finish a command that either sets or puts a character...@>= 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; lgp_v:=v; {make sure that |lgp_v| holds the current baseline coordinate} if o>=put1 then goto done; if q=invalid_width then q:=0 else hh:=hh+pixel_round(q); if q=0 then lgp_h:=0 {must force \.{LGP} repositioning after zero width} else lgp_h:=h+q; {set up value to match what |move_right| will produce} goto move_right @ There is an interesting point here, that caused a bug until it was noticed. The actual rectangle defined for a rule uses the function |rule_pixels| to adjust for floating-point error, but this function can not be used to update the spacing, or we will have trouble with certain mixtures of rules and characters. The spacing is updated by means of the same |pixel_round| function that is used for everything else except rules. @<Finish a command that either sets or puts a rule...@>= q:=signed_quad; escape_string("m"); if right_reading then begin send_in_low_high_sequence(hh); send_in_low_high_sequence(vv); end else begin send_in_low_high_sequence(vv); send_in_low_high_sequence(hh); end; if showing then begin print(' height ',p:1,', width ',q:1); if (p<=0)or(q<=0) then print(' (invisible)') else print(' (',rule_pixels(p):1,'x',rule_pixels(q):1,' pixels)'); end; escape_string("r"); i:=hh+rule_pixels(q); if right_reading then begin j:=vv+rule_pixels(p); {any change in y is explicit in the {\tt DVI} file} send_in_low_high_sequence(i); send_in_low_high_sequence(j); end else begin j:=vv-rule_pixels(p); {height of rotated rule is to the left} send_in_low_high_sequence(j); send_in_low_high_sequence(i); end; @^Draw a Rectangle@> if o=put_rule then goto done; if out_mode > terse then print_ln(' '); p:=q; {that's what |out_space| expects} out_space('rule spacing'); @.rule spacing@> @ Since the parent program, \.{DVItype}, is intended to diagnose strange errors, it checks carefully to make sure that |h| and |v| do not get out of range. Normal \.{DVI}-reading programs need not do this, but it comes in handy at unexpected times, so we keep the code active. @<Finish a command that sets |h:=h+q|, then |goto done|@>= if (h>0)and(q>0) then if h>infinity-q then begin error('arithmetic overflow! parameter changed from ', @.arithmetic overflow...@> q:1,' to ',infinity-h:1); q:=infinity-h; end; if (h<0)and(q<0) then if -h>q+infinity then begin error('arithmetic overflow! parameter changed from ', q:1, ' to ',(-h)-infinity:1); q:=(-h)-infinity; end; if showing then begin print(' h:=',h:1); if q>=0 then print('+'); print(q:1,'=',h+q:1,', hh:=',hh:1); end; h:=h+q; if abs(h)>max_h_so_far then begin max_h_so_far:=abs(h); if out_mode=errors_only then max_h_so_far:=max_h_so_far + warn_pixel; {reduce the incidence of these messages by using a |warn_pixel| threshold} if abs(h)>max_h then error('warning: |h|>',max_h_so_far:1,'!'); @.warning: |h|...@> end; goto done @ @<Finish a command that sets |v:=v+p|, then |goto done|@>= if (v>0)and(p>0) then if v>infinity-p then begin 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; if showing then begin print(' v:=',v:1); if p>=0 then print('+'); print(p:1,'=',v+p:1,', vv:=',vv:1); end; v:=v+p; if abs(v)>max_v_so_far then begin max_v_so_far:=abs(v); if abs(v)>max_v then error('warning: |v|>',max_v_so_far:1,'!'); @.warning: |v|...@> end; goto done @ @<Show the values of |ss|, |h|, |v|, |w|, |x|, |y|, |z|...@>= if showing then begin print_ln(' '); print('level ',ss:1,':(h=',h:1,',v=',v:1, ',w=',w:1,',x=',x:1,',y=',y:1,',z=',z:1, ',hh=',hh:1,',vv=',vv:1,')'); end; goto done @ @<Finish a command that changes the current font...@>= font_num[nf]:=p; cur_font:=0; while font_num[cur_font]<>p do incr(cur_font); escape_string("f"); output_byte(cur_font); if showing then begin print(' current font is '); print_font(cur_font); end; goto done @^Set Font@> \f @* Rotating the bit-map. For certain applications such as the making of transparencies, it is useful to have the capacity to set in landscape mode. For this we must turn the bit-map around $90\deg$. @ @<Pick up column bits...@>= begin bit_rows:=((vs+15) div 16) * 16; {vertical height of envelope, or number of rows, rounded up} n:=row_offset; repeat @<Record each group of eight bits in reverse order and send to the \.{LGP}@>; until bit_rows <= 0; incr(clump_columns); if clump_columns >= clump_limit then begin clump_columns:=0; {ready to start again} clump_limit:=8; {always reset to 8 after first pass which might be smaller} decr(row_offset); {begin from a different starting point} end; decr(bit_columns); end @ This needs a couple of definitions to make it look right. The two opposing shifts take care of the bit reversal for the \.{LGP}. @d shift_left(#)==#:=#+#; {shift left by addition} @d shift_right(#)==#:=# div 2; {and right by integer division} @<Record each group...@>= begin rotated_byte:=0; for k:=1 to 8 do begin shift_left(rotated_byte); if odd(glyph_bytes[n]) then rotated_byte:=rotated_byte + 1; shift_right(glyph_bytes[n]); n:=n + glyph_step; decr(bit_rows); end; output_byte(bits_reversed[rotated_byte]); end @ We need not waste time and font storage space on the zero-fill in the loose-packed \.{PXL} format. We go to the last significant byte in each row, starting from |row_offset| and incrementing the index of our temporary array by |glyph_step|. We pass over and over through the designated bytes, shifting continually to the right, until we have brought a non-zero bit into the low-order position of at least one row. @<Read bit-columns from right...@>= begin non_pixels:=(hs_in_bytes*8)-hs_in_pixels; clump_limit:=8-non_pixels; {same as |hs_in_pixels mod 8|--significant bits in final byte of each row} if non_pixels > 0 then begin n:=row_offset; repeat for m:=1 to non_pixels do shift_right(glyph_bytes[n]); n:=n+glyph_step; until n > (vs*hs*4); {count of bytes in loose-packed \.{PXL} format} end end @ @<Copy |glyph_bytes|...@>= begin for k:=1 to 10000 do glyph_bytes[k]:=0; n:=0; for k:=1 to hs*vs do begin read_pxl_bytes; glyph_bytes[n]:=b0; incr(n); glyph_bytes[n]:=b1; incr(n); glyph_bytes[n]:=b2; incr(n); glyph_bytes[n]:=b3; incr(n); end end \f @* Skipping pages. A routine that's much simpler than |do_page| is used to pass over pages that are not being translated. The |skip_pages| subroutine is assumed to begin just after the preamble has been read, or just after a |bop| has been processed. It continues until either finding a |bop| that matches the desired starting page specifications, or until running into the postamble. For the moment, this routine is unused, so we isolate it in comment braces. It would be wasteful to remove it altogether, since it may come in handy for more sophisticated versions of \.{DVI2LGP}. @p @{ procedure skip_pages; label 9999; {end of this subroutine} var p:integer; {a parameter} @!k:0..255; {command code} @!down_the_drain:integer; {garbage} begin showing:=false; while true do begin if eof(dvi_file) then bad_dvi('the file ended prematurely!'); @.the file ended prematurely@> k:=get_byte; p:=first_par(k); case k of bop: begin @<Pass a |bop| command, setting up the |count| array@>; if not started and start_match then begin started:=true; goto 9999; end; end; set_rule,put_rule: down_the_drain:=signed_quad; fnt_def1,fnt_def1+1,fnt_def1+2,fnt_def1+3: define_font(down_the_drain); xxx1,xxx1+1,xxx1+2,xxx1+3: while p>0 do begin down_the_drain:=get_byte; decr(p); end; post: begin in_postamble:=true; goto 9999; end; othercases do_nothing endcases; end; 9999:end; @} @ Global variables called |old_backpointer| and |new_backpointer| are used to check whether the back pointers are properly set up. Another one tells whether we have already found the starting page. @<Glob...@>= @!old_backpointer:integer; {the previous |bop| command location} @!new_backpointer:integer; {the current |bop| command location} @!started:boolean; {has the starting page been found?} @ @<Set init...@>= old_backpointer:=-1; started:=false; @ When the \.{DVI} file is being read sequentially, in what we paradoxically call |backwards| mode, it is possible to make an extra check for valid file organization here, but if we actually make use of the backpointers to read the file last to first (so that it will come out sorted on the output tray) this check becomes meaningless. The need for full debugging information, incidentally, explains why the two debugging modes have a default setting of |backwards|. The convenient default for production mode is, unfortunately, system dependent, since it calls on the |move_to_byte| procedure. @^system dependencies@> @<Pass a |bop|...@>= if backwards then new_backpointer:=cur_loc-1; incr(page_count); for k:=0 to 9 do count[k]:=signed_quad; if backwards then begin if signed_quad<>old_backpointer then print_ln('backpointer in byte ',cur_loc-4:1, ' should be ',old_backpointer:1,'!'); @.backpointer...should be p@> old_backpointer:=new_backpointer; end else new_backpointer:=signed_quad @* Using the backpointers. First comes a routine that illustrates how to find the postamble quickly. @<Find the postamble, working back from the end@>= n:=dvi_length; if n<53 then bad_dvi('only ',n:1,' bytes long'); @.only n bytes long@> m:=n-4; repeat if m=0 then bad_dvi('all 223s'); @.all 223s@> move_to_byte(m); k:=get_byte; decr(m); until k<>223; if k<>id_byte then bad_dvi('ID byte is ',k:1); @.ID byte is wrong@> move_to_byte(m-3); q:=signed_quad; if (q<0)or(q>m-33) then bad_dvi('post pointer ',q:1,' at byte ',m-3:1); @.post pointer is wrong@> move_to_byte(q); k:=get_byte; if k<>post then bad_dvi('byte ',q:1,' is not post'); @.byte n is not post@> post_loc:=q; first_backpointer:=signed_quad @ Note that the last steps of the above code save the locations of the the |post| byte and the final |bop|. We had better declare these global variables, together with another one that we will need shortly. @<Glob...@>= @!post_loc:integer; {byte location where the postamble begins} @!first_backpointer:integer; {the pointer following |post|} @!start_loc:integer; {byte location of the first page to process} @!back_count:integer; {reverse count for simple page positioning} @ The next little routine shows how the backpointers can be followed to move through a \.{DVI} file in reverse order. It would of course be simpler and faster just to read the whole file from the beginning but we would lose some rather useful features in that case. The chief of these is the ability to read back-to-front and stack the output pages on the tray in the expected order of reading. This is the default format for production runs of the program. There is one trick to that, however. When we have a long file of pages which all have the same page number in \TeX\ \.{count0}, we have to keep the program from backing up to the preamble when it is in default mode. In |backwards| mode, we keep changing the |start_loc| byte indicator every time |start_match| returns |true|, but in default mode, we prevent any further change after the first |start_match|. Note that the use of the |move_to_byte| procedure makes this module system dependent, however. @^system dependencies@> @<Count the pages and move to the starting page@>= if not backwards then if start_on_page>0 then decr(start_on_page); {a little adjustment that turned out to be necessary} if backwards then back_count:=total_pages-start_on_page+1 else begin if ((start_on_page + max_pages)<total_pages) then back_count:=total_pages-start_on_page-max_pages+1 else back_count:=1; end; q:=post_loc; p:=first_backpointer; start_loc:=-1; if p>=0 then 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@> for k:=0 to 9 do count[k]:=signed_quad; if page_count=back_count then begin start_there[0]:=true; start_count[0]:=count[0]; end; {force a match} if start_match then begin if backwards then start_loc:=q {keep on looking for first page} else begin if start_loc=-1 then start_loc:=q {in this instance, first match wins} end end; p:=signed_quad; until p<0; if page_count<>total_pages then print_ln('there are really ',page_count:1,' pages, not ',total_pages:1,'!'); @.there are really n pages@> if start_loc<0 then abort('starting page number could not be found!'); move_to_byte(start_loc+1); old_backpointer:=start_loc; for k:=0 to 9 do count[k]:=signed_quad; new_backpointer:=signed_quad; started:=true @* Reading the postamble. Now imagine that we are reading the \.{DVI} file and positioned just four bytes after the |post| command. That, in fact, is the situation, when the following part of \.{DVI2LGP} is called upon to read, translate, and check the rest of the postamble. @p procedure read_postamble; var k:integer; {loop index} @!p,@!q,@!m:integer; {general purpose registers} begin showing:=false; post_loc:=cur_loc-5; if not take_defaults then print_ln('Postamble starts at byte ',post_loc:1,'.'); @.Postamble starts at byte n@> 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;@/ if not take_defaults then print('maxv=',max_v:1,', maxh=',max_h:1);@/ max_s:=get_two_bytes; total_pages:=get_two_bytes;@/ if not take_defaults then print_ln(', maxstackdepth=',max_s:1,', totalpages=',total_pages:1); @<Process the font definitions of the postamble@>; @<Make sure that the end of the file is well-formed@>; end; @ When we get to the present code, the |post_post| command has just been read. @<Make sure that the end of the file is well-formed@>= q:=signed_quad; if q<>post_loc then print_ln('bad postamble pointer in byte ',cur_loc-4:1,'!'); @.bad postamble pointer@> m:=get_byte; if m<>id_byte then print_ln('identification in byte ',cur_loc-1:1, @.identification...should be n@> ' should be ',id_byte:1,'!'); k:=cur_loc; m:=223; while (m=223)and not eof(dvi_file) do m:=get_byte; if not eof(dvi_file) then bad_dvi('signature in byte ',cur_loc-1:1, @.signature...should be...@> ' should be 223!') else if cur_loc<k+4 then print_ln('not enough signature bytes at end of file (', @.not enough signature bytes...@> cur_loc-k:1,')'); @ @<Process the font definitions...@>= open_lgp_file; {it's going to be needed now} repeat k:=get_byte; if (k>=fnt_def1)and(k<fnt_def1+4) then begin p:=first_par(k); define_font(p); if out_mode>errors_only then print_ln(' '); k:=nop; end; until k<>nop; if k<>post_post then print_ln('byte ',cur_loc-1:1,' is not postpost!') @.byte n is not postpost@> @* The main program. Now we are ready to put it all together. This is where \.{DVI2LGP} starts, and where it ends. The parent program contains two short tests to determine whether the |postamble| can be read first. Since the calculations for shifting output left, center or right require some information from the postamble, we have eliminated those tests. This program always reads the postamble before starting on the main text. @p begin initialize; {get all variables initialized} dialog; {set up all the options} @<Process the preamble@>; @<Find the postamble, working back from the end@>; in_postamble:=true; read_postamble; in_postamble:=false; @<Count the pages and move to the starting page@>; @<Position output left, center or right on output page@>; if not in_postamble then @<Translate up to |max_pages| pages@>; escape_string("e"); {finish the \.{LGP} file} if clever then @<Set up prompt for next step@>; final_end:end. @ The main program needs a few global variables in order to do its work. @<Glob...@>= @!k,@!m,@!n,@!p,@!q:integer; {general purpose registers} @ A \.{DVI}-reading program that reads the postamble first need not look at the preamble; but \.{DVI2LGP} looks at the preamble in order to do error checking, and to display the introductory comment. @<Process the preamble@>= open_dvi_file; p:=get_byte; {fetch the first byte} if p<>pre then bad_dvi('First byte isn''t start of preamble!'); @.First byte isn't...@> p:=get_byte; {fetch the identification byte} if p<>id_byte then print_ln('identification in byte 1 should be ',id_byte:1,'!'); @.identification...should be n@> @<Compute the conversion factor@>; p:=get_byte; {fetch the length of the introductory comment} if interaction>batch_mode then begin print(''''); while p>0 do begin decr(p); print(xchr[get_byte]); end; print_ln(''''); end @ Here are some globals to store values from the postamble, along with some other values derived from them. @<Glob...@>= @!conv: real; {converts \.{DVI} units to pixels} @!true_conv: real; {converts unmagnified \.{DVI} units to pixels} @!numerator,@!denominator: integer; {stated conversion ratio} @!mag: integer; {magnification factor times $1000$} @!warn_pixel: integer; {threshold for reporting right margin overrun} @ The conversion factor |conv| is figured as follows: There are exactly |n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch, and |resolution| pixels per inch. Then we have to adjust this by the stated amount of magnification. @<Compute the conversion factor@>= numerator:=signed_quad; denominator:=signed_quad; if numerator<=0 then bad_dvi('numerator is ',numerator:1); @.numerator is wrong@> if denominator<=0 then bad_dvi('denominator is ',denominator:1); @.denominator is wrong@> if not take_defaults then print_ln('numerator/denominator=',numerator:1,'/',denominator:1); conv:=(numerator/254000.0)*(resolution/denominator); warn_pixel:=trunc(threshold/conv); {threshold for reporting right-margin overrun}@t}% --CHECKME--RKF @>@/ if out_mode=errors_only then max_h_so_far:=max_h+warn_pixel; {give a right-margin overrun warning only for significant amounts} 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); if not take_defaults then print_ln('magnification=',mag:1,'; ',conv:16:8,' pixels per DVI unit') @ It is possible to shift the output frame to the left, center or right of the paper sheet, by specifying |xchr[sw_c]|L, |xchr[sw_c]|C or |xchr[sw_c]|R in the option switch line. @d both_margins==physical_margin + right_margin @<Position output left...@>= begin @<Position rotated output if necessary@>; if right_reading then nett_width:=full_width-both_margins else nett_width:=page_pixel_height; {\.{LGP} will quite happily print on bottom line of page without clipping} left_margin:=physical_margin; {initialize for the minimum} {protect left overshoot with a minimum pixel count at left edge} if margin_set <> "L" then margin_count:=nett_width - pixel_round(max_h); if margin_count < 0 then begin margin_count := 0; { Keep it safe! } print_ln('Page is already too wide. I cannot add a margin '); print_ln('I will proceed as if you had specified the ',xchr[sw_c],'L margin'); margin_set:="L"; {and will even reset the switch to prove it} end; if margin_set = "C" then begin left_margin := margin_count div 2; if (not right_reading) and (left_margin > both_margins) then left_margin:=left_margin+physical_margin; {Shove output to top of page by amount of hardware margin, but don't overrun} end; if margin_set = "R" then left_margin:=margin_count; if not take_defaults then begin print('Left margin is '); print_ln(left_margin:1,' pixels wide.'); end; margin_count:=0; end @ @<Position rotated...@>= begin if right_reading then top_margin:=normal_top else begin top_margin:=(full_width-pixel_round(max_v)) div 2; if top_margin < physical_margin then top_margin:=physical_margin; end end @ 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. Since we have chosen to make back to front reading the default for normal production runs, we have to confess that this module is system dependent. @^system dependencies@> @<Translate up to...@>= begin while max_pages>0 do begin decr(max_pages); if interaction>batch_mode then begin if out_mode > errors_only then print_ln(' '); print(cur_loc-45:1,': beginning of page '); end; @.beginning of page@> for k:=0 to start_vals do begin print(count[k]:1); if k<start_vals then print('.') else print_ln(' '); end; if not do_page then bad_dvi('page ended unexpectedly!'); @.page ended unexpectedly@> if not backwards then if (new_backpointer<>-1) then move_to_byte(new_backpointer) else goto done; repeat k:=get_byte; if (k>=fnt_def1)and(k<fnt_def1+4) then begin p:=first_par(k); define_font(p); k:=nop; end; until k<>nop; if k=post then begin in_postamble:=true; goto done; end; if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop'); @.byte n is not bop@> @<Pass a |bop|...@>; end; done:end @ Here we provide a prompt at the close of the program, which sets up a command line for the next step and tricks the operating system into thinking the user typed it. If the line looks right, it takes no more than a simple carriage return to initiate the next step of processing. This sort of thing is, of course, not even remotely possible in standard \PASCAL, and all we can really do outside the limits of acutely system dependent programming is set up the prompt array. Then you are on your own. @d prompt_length=80 {or whatever terminal width you want to simulate} @d prompt_name=='TLGP' {change to whatever suits you} @d prompt_name_length=4 {this is set for 'TLGP'; change as needed} @<Glob...@>= @!prompt_array:packed array[1..prompt_name_length] of char; @!prompt_string:packed array[1..prompt_length] of char; @ @<Set up prompt for next step@>= prompt_array:=prompt_name; for p:=1 to prompt_length do prompt_string[p]:=' '; for p:=1 to prompt_name_length do prompt_string[p]:=prompt_array[p]; q:=prompt_name_length+2; {leave a space after the command} p:=1; while lgp_name[p]<>' ' do begin prompt_string[q]:=lgp_name[p]; incr(p); incr (q); end;@/ {a system-dependent module goes here} @* Special {\tentex LGP} functions. There are several rather useful features of the \.{LGP} that are not part of Vanilla \TeX\ but must be reached through the \TeX\ \.{\\special} escape. We have reserved these for a new chapter, to avoid confusion in the main program, which is devoted to the essentials of \TeX. The vector drawing feature, which produces a line one pixel thick from any arbitrary point on the bit-map to any other point, is made available for the user of \TeX\ through the use of two calls on the \.{\\special} primitive. The first call sets the starting point for the vector, and reads \.{\\special\{begin-vector\}}. This must precede the \.{\\special\{vector\}} command, which actually draws the vector. The two commands need not be contiguous but, for obvious reasons, they must be on the same page. If the paging operation of \TeX\ accidentally separates a vector-start from its associated vector-draw command, the vector-draw command will be cancelled. The program also recognizes \.{\\special\{overlay=}\\{filename}\.{\}} for the ``forms overlay'' feature, as well as \.{\\special\{raster-data=}\\{filename}\.{\}} and \.{\\special\{screen-image=}\\{filename}\.{\}} for reading predigested graphics. The ``forms overlay'' feature doesn't seem to have been implemented in the \.{LGP} hardware, however, so the code implementing that feature is of no real use at present. We haven't yet got around to the other two, but the framework is in place. @ We will need some extra globals. @<Glob...@>= @!spec_file:packed file of eight_bits; {a previously prepared LGP file} @!lgp_spec_file_name:packed array[1..name_length] of text_char; @ @<Check for valid \.{LGP} special@>= case spec_string[1] of "b":@<Store starting point of vector@>; "v":@<Complete vector drawing command@>; "o":@<Bring in a forms overlay@>; "r":@<Get a block of raster data from a file@>; "s":@<Get a block of screen-image data from a file@>; endcases @ The full form of this is \.{\\special\{begin-vector\}}, but we only check for the word \.{begin} and a correct string length. @<Store starting point of vector@>= begin if ((spec_string[2]="e") and (spec_string[3]="g") and (spec_string[4]="i") and (spec_string[5]="n") and (p=12)) then begin hh_vector:=hh; vv_vector:=vv; { (already checked for range) } end; end @ The command for this is \.{\\special\{vector\}}. @<Complete vector drawing command@>= begin if ((spec_string[2]="e") and (spec_string[3]="c") and (spec_string[4]="t") and (spec_string[5]="o") and (spec_string[6]="r")) then begin if (hh_vector=infinity) or (vv_vector=infinity) then begin print_ln('--you haven''t set a start for this vector!'); print_ln ('Cancelled---use \special{begin-vector} before \special{vector}') end else begin if showing then print(' vector from hh= ',hh_vector:1, ' and vv= ', vv_vector:1, ' to hh=', hh:1, ' and vv= ', vv:1); escape_string("m"); {reposition \.{LGP} to vector start} if right_reading then begin send_in_low_high_sequence(hh_vector); send_in_low_high_sequence(vv_vector); end else begin send_in_low_high_sequence(vv_vector); send_in_low_high_sequence(hh_vector); end; escape_string("v"); if right_reading then begin {\.{LGP} gets repositioned to location} send_in_low_high_sequence(hh); {at which \.{\\special\{vector\}} } send_in_low_high_sequence(vv); {was called in \TeX\ file} end else begin send_in_low_high_sequence(vv); send_in_low_high_sequence(hh); end; hh_vector:=infinity; vv_vector:=infinity; end end; end @ @<Set up |lgp_spec_file_name|@>= begin if (spec_string[k]>="a") or (spec_string[k]<="z") then begin lgp_spec_file_name[n]:=system_case_of(spec_string[k]); end else lgp_spec_file_name[n]:=xchr[spec_string[k]]; incr(k); end @ The command for this is \.{\\special\{overlay=}\\{filename}\.{\}}. @<Bring in a forms overlay@>= begin if ((spec_string[2]="v") and (spec_string[3]="e") and (spec_string[4]="r") and (spec_string[5]="l") and (spec_string[6]="a") and (spec_string[7]="y") and (spec_string[8]="=")) then begin for k:=1 to name_length do lgp_spec_file_name[k]:=' '; k:=9; for n:=1 to p-8 do @<Set up |lgp_spec_file_name|@>; @<Get the overlay@>; end end @ An overlay file should look more or less like a normal \.{LGP} file, but it can only be one page long, and it must terminate with an \.{<ESC> p} sequence. @d escape=@'033 {{\mc ASCII} \.{ESC} character} @<Get the overlay@>= begin open_spec_file; if not eof(spec_file) then repeat if eof(spec_file) then begin error(' incomplete overlay file!'); goto 9998; end; p:=get_spec_byte; if (q=escape) then end_of_special:=(p="p"); output_byte(p); q:=p; until end_of_special end; @ The full form of this is \.{\\special\{raster-data=}\\{filename}\.{\}}, but we only check for the word \.{raster} and a correctly positioned `$=$' character. The file will take the form of a fully pre-prepared ``Define a Block of Raster Data'' sequence. @^Define a Block of Raster Data@> @<Get a block of raster data from a file@>= begin if ((spec_string[2]="a") and (spec_string[3]="s") and (spec_string[4]="t") and (spec_string[5]="e") and (spec_string[6]="r") and (spec_string[12]="=")) then {should check it all, but enough is enough!} begin do_nothing end end @ The full form of this is \.{\\special\{screen-image=}\\{filename}\.{\}}, but we only check for the word \.{screen} and a correctly positioned `$=$' character. The file will be of the form described in the \\{Symbolics Inc.} manual under ``Print a Screen Image.'' @^Print a Screen Image@> @<Get a block of screen-image data from a file@>= begin if ((spec_string[2]="c") and (spec_string[3]="r") and (spec_string[4]="e") and (spec_string[5]="e") and (spec_string[6]="n") and (spec_string[13]="=")) then begin do_nothing end end @ We use the simple |get_spec_byte| function to read the next byte or bytes from the designated |spec_file|. This is exactly the same function as the one used for |dvi_file| reading, but since the file name is explicitly declared, we take the easy way out and copy it here. @^system dependencies@> @<Procedures for \.{LGP}...@>= function get_spec_byte:integer; {returns the next byte, unsigned} var b:eight_bits; begin if eof(spec_file) then get_spec_byte:=0 else begin read(spec_file,b); get_spec_byte:=b; end; end; @# procedure open_spec_file; begin reset(spec_file,lgp_spec_file_name); end; @* System-dependent changes. This module should be replaced, if necessary, by changes to the program that are necessary to make \.{DVI2LGP} work at a particular installation. It is usually best to design your change file so that all changes to previous modules preserve the module numbering; then everybody's version will be consistent with the printed program. More extensive changes, which introduce new modules, can be inserted here; then only the index itself will get a new module number. @^system dependencies@> @* Index. Pointers to error messages appear here together with the section numbers where each ident\-i\-fier is used. SHAR_EOF fi # end of overwriting check if test -f 'lgpext.h' then echo shar: will not over-write existing file "'lgpext.h'" else cat << \SHAR_EOF > 'lgpext.h' (* borrowed from Symbolics' randomio.h *) function curpos(var fileptr:bytefile):integer; external; procedure setpos(var fileptr:bytefile; n: integer); external; (* borrowed from Howard Trickey's dvip *) procedure opendvifile; external; function getbyte: integer; external; function signedbyte: integer; external; function gettwobytes: integer; external; function signedpair: integer; external; function getthreebytes: integer; external; function signedtrio: integer; external; function signedquad: integer; external; (* modified from dvip *) function curdvipos(var f:bytefile):integer; external; procedure setdvipos(var f:bytefile;n:integer); external; (* and more routines to speed things up *) procedure openlgpfile; external; procedure outputbyte(b: integer); external; function readbyte(var f: bytefile): integer; external; function testeof(var f: bytefile): boolean; external; (* now, some routines to implement paths for file names. These are borrowed from Howard Trickey's dvitype *) procedure setpaths; external; function testaccess(accessmode:integer; filepath:integer): boolean; external; SHAR_EOF fi # end of overwriting check if test -f 'dvi2lgp.1' then echo shar: will not over-write existing file "'dvi2lgp.1'" else cat << \SHAR_EOF > 'dvi2lgp.1' .ds TX T\h'-.1667m'\v'.22m'E\h'-.125m'\v'-.22m'X .TH DVI2LGP 1 12/5/83 .SH NAME dvi2lgp \- translate from .dvi file to .lgp file .SH SYNOPSIS .B dvi2lgp .PP .B dvi2lgp dvifile[.dvi] .SH DESCRIPTION The DVI2LGP utility program reads binary device-independent (``.dvi'') files that are produced by document compilers such as \*(TX, and converts them into a preprocessed .lgp file in ``native-mode'' coding. This program has two chief purposes: .RS .5i .TP 5 (1) It assembles and outputs a sequential file of LGP codes, and .TP 5 (2) It can be used to determine whether a .dvi file is valid or invalid, since it retains the symbolic output form of its parent, DVItype. .RE .PP There are two ways to invoke the program. When the program is invoked with a file name on the command line, translation of the .dvi file to make a .lgp file starts immediately. This is called ``default mode'' because default values are assigned to various options. If no arguments are placed on the command line, then the program will inquire about the desired settings for a number of options before translation begins. .PP DVI2LGP locates its font bit maps by looking in the directories specified in the search path found in the TEXFONTS environment variable. This is the same variable that TeX uses to locate its font metric files. If TEXFONTS is not defined, then a default value is used. The entire path must be no longer than 100 characters long. You can change the value of this environment with the .I csh command (for example) .br .in +2 setenv TEXFONTS .:/usr/lib/tex/fonts .in -2 or the .I sh command sequence .br .in +2 TEXINPUTS=.:/usr/lib/tex/fonts .br export TEXINPUTS .in -2 .br both of which will cause DVI2LGP to look for its bit maps first in the current directory and then in /usr/lib/tex/fonts. .PP The rest of this description explains DVI2LGP's processing modes in more detail. .PP The DVI2LGP processor attempts first of all to provide a simple and direct method for interpreting .dvi files for the Symbolics Inc. LGP-1 printer. In the default mode, we assume that the user wants an entire file printed, at true size, and delivered to the output tray in correct order for reading (i.e., backwards order for printing). If the user finds the default mode insufficent, there is a wide range of options to produce various forms of standard \*(TX output, as well as several forms of diagnostic output which can be used to check on the correctness of the .dvi file. Finally, it is possible to call on certain special features of the LGP which are outside the normal functions of \*(TX. These are not part of the \*(TX typesetting system, but they may be added to an otherwise normal .dvi file through the use of the \*(TX \\special escape primitive. Such usage will, of course, be absolutely specific to the LGP printer, but a .dvi file containing ``\\special'' sequences can safely be run through any properly designed .dvi interpreter. The presence of the sequence will be noted in the diagnostic log file, but no action will be taken (unless the interpreter uses exactly the same \\special for some other purpose). .PP In default mode, we assume that the input filename argument appears on the command line with the program call name. If the program senses the presence of the input filename on the command line, and finds no other flags or switch characters, the processing begins at once and selects defaults values for all options. If the input filename already has the extension .dvi, this extension need not be supplied in the command line, since it is assumed as the default. Whatever the input filename may be, the program in all cases generates an output filename with the extension .lgp and creates this file on the user's current working directory, (even if the input file has been taken from some other directory or structure). Thus, the first stages of a Unix transaction will look like this: .RS .5i .br This is DVI2LGP, Version n, last modified May 29, 1453 .br [sample.dvi->sample.lgp] .br .RE and the first stages of the transaction on other computers will be similar, except that the file names may be folded into uppercase characters. .PP After the program has started running in default mode, the .dvi preamble banner line is displayed on the terminal from which the job was started, followed by a progress report which marks the occurrence of each beginning of page marker in the .dvi file. A completed job creating an .lgp file for the printer looks like this: .RS .5i .br % dvi2lgp sample .br This is DVI2LGP, Version n, last modified May 29, 1453 .br [sample.dvi->sample.lgp] .br \' TeX output 1471.7.12:1200\' .br 9296: beginning of page 3 .br 5066: beginning of page 2 .br 42: beginning of page 1 .br % .br .RE .PP On the Unix system, you would then spool the .lgp file to the printer with the command .RS .5i .br % lgp sample.lgp .br .RE (On some systems, such as Tops-20, we also set up the next command---this is generated by the program---so that all that is needed is to respond to it with a carriage return and the LGP file will be queued to the printer, assuming that your system has provided a protocol for this queuing operation. We may invoke the subsequent .B lgp command directly in later versions of this software on Unix.) .PP If the command line does not include the input filename, then the full range of options becomes accessible. The user is first given the opportunity to redirect the diagnostic output (including the font identifications and the beginning-of-page notifications) to a file. Then the program asks for an input filename, which may be supplied with or without the .dvi extension. As in the default mode, the output filename is automatically generated from the input filename, and the file is created on the user's current working directory. .PP At this point the user has three choices: .RS .5i .TP 5 1. The remaining default options can again be selected by typing a carriage return in response to the prompt line, which reads: .RS .5i .br .ce 1 CR to accept default of all pages, no magnification. ? for help. .RE .TP 5 2. A display of help messages can be called up by typing a question mark. .TP 5 3. The full sequence of option specification queries can be started by typing any visible ASCII character other than a question mark. .RE .PP The first cluster of options must be specified all on the one line and, in the present version of the program, may not have any spaces between options. Each distinct option is introduced by a ``switch character'' which may differ from system to system. .PP The following single-letter options exist: .PP For position on the output sheet: .RS .5i .br C---Center. Equal margins right and left, as nearly as possible. This is the default. .br L---Left. Text pushed over to the left margin as close as the LGP hardware allows. .br R---Right. Text pushed over to the right margin, leaving as wide a left margin as the LGP hardware allows. .br .RE .PP For orientation on the output sheet: .RS .5i .br V---Vertical. Normal, portrait mode output. This is the default. .br H---Horizontal. Landscape mode output rotated ninety degrees. .br .RE .PP For sequential output in .dvi order (which will stack up on the tray in inverted order): .RS .5i .br I---Inverted. The default has no name. .br .RE .PP For different amounts of diagnostic output: .RS .5i .br P---Production. The minimum described above as the default. .br T---Terse. Rather more information about fonts. .br S---Some-debug. Line by line reports of progress. .br F---Full-debug. Exhaustive, byte-by-byte reports. .br .RE .PP In addition to these, it is also possible to specify, on the same line, and with no intervening spaces, a simple selection of pages for output. This option (assuming that `-' is the switch character) takes the forms -<n> or -<n>:<m> or -<n>:end. Thus, it is possible to select a single page only, or a range of pages, or a range of pages through to the end of the file. Note that this option does not use the values in the \*(TX counters (which often contain page numbers). When you use this option, you are simply telling the program to count in sequentially from the first to the <n>th page of the file, to start there and to continue processing until the <m>th page of the file or the end of the file is reached. The use of this option automatically sets the I(nverted) mode switch. .PP When you see the prompt reading: .RS .5i .br .ce 1 Starting page (default = *) .br .RE you can make use of the specific values in \*(TX's count registers. After you have decided what you wish to do with that option, you have a second chance to tell the program how many pages you really want to generate on the printer. The default is conservatively set at a million, unless you have already used the -<n>:<m> option described above. .PP The last, but perhaps the most interesting option allows you to magnify the entire output page by whatever magnification you think you can get away with. The limitation here is fonts. If you have fonts corresponding to really wierd percentages of magnification then you can use such values as a response to this option prompt. Normally you will want to use magsteps instead. The principle behind magsteps is part of \*(TX itself, and you can look it up in the \*(TX book. .PP The use of \\special functions is really part of \*(TX rather than part of this driver. We have not completed the work on these yet, but the LGP vector-drawing function is available. For this, you should include the \*(TX commands \\special{begin-vector} at the point where you want the vector to begin, and later on give the command \\special{vector}. For obvious reasons, these commands have to be paired on the same page. But don't worry if the paging operation of \*(TX happens to split them up unexpectedly; the operations will simply be cancelled, and you will be told about it. It would be nice to get \\special{overlay} working, but at present we cannot test it, so it remains in skeletal form for now, as does \\special{raster-data} and \\special{screen-image}. .SH "ENVIRONMENT" .IP TEXFONTS Search path for font bit maps (PXL files, so called because they have an .nnnnpxl extension). The entire path must be no longer than 100 characters long. The default doesn't include the current area (".") to avoid confusing the programs that convert the output for printing on the various output devices (most of which don't know about the path stuff yet). Default: /usr/lib/tex/fonts .SH "SEE ALSO" tex(1), lgp(1) .SH "BUGS" At present, the command line processing is primitive. Switch processing needs to be added. Also, it is not possible to print a .dvi file which does not have a filename extension. .PP The .dvi file format supported is Version 2. Older versions of the .dvi file (such as those produced by older versions of \*(TX) are not supported and cannot be printed using this program.) .PP Bugs should be reported to Richard Furuta or Pierre MacKay at the University of Washington. .SH "AUTHORS" Pierre MacKay and Richard Furuta. SHAR_EOF fi # end of overwriting check if test -f 'lgpext.c' then echo shar: will not over-write existing file "'lgpext.c'" else cat << \SHAR_EOF > 'lgpext.c' /* ---routines which are required because pc won't do it--- */ /* borrowed from Symbolics' randomio.c */ #include "../../h00vars.h" /* defines Pascal I/O structure */ #include <sys/types.h> #include <sys/stat.h> /* number of bytes in the file */ curpos(curfile) register struct iorec *curfile; { struct stat st; if (fstat(fileno(curfile->fbuf), &st) == -1) PERROR("Could not stat ", curfile->pfname); else return (st.st_size); } /* * This is a variant of SEEK in libpc. */ setpos(curfile, loc) int loc; register struct iorec *curfile; { int n; curfile->funit |= SYNC; curfile->funit &= ~(EOFF|EOLN|SPEOLN); if (loc < 0) n = fseek(curfile->fbuf, 0, 2); else n = fseek(curfile->fbuf, loc, 0); if (n == -1) PERROR("Could not seek ", curfile->pfname); } /* ---routines provided for efficiency reasons--- */ /* if you find yourself porting this code to another machine, you will probably want to replace all of these routines with Pascal routines, at least initially. Don't forget that eofdvifile, a variable below, really represents eof(dvi_file). */ /* borrowed from Howard Trickey's dvip */ /* External procedures for use with dvip. Written by H. Trickey, 9/3/83. Implement the following procedures (as declared in Pascal) procedure opendvifile; function getbyte: integer; function signedbyte: integer; function gettwobytes: integer; function signedpair: integer; function getthreebytes: integer; function signedtrio: integer; function signedquad: integer; */ #include <stdio.h> static FILE *dvif; extern int eofdvifile; /* needs to be set true when this happens */ extern int curloc; /* needs to be incremented by getbyte, signedbyte */ extern char dviname[]; /* open the dvifile, using the name in dvifilename (modified from hwt by rkf) */ opendvifile() { char lcldviname[BUFSIZ]; /* for a local copy of the dvi name */ int ctr; strcpy(lcldviname,dviname); /* a local copy to play with */ /* the dvi name terminates on the first blank */ for(ctr = 0; (ctr < BUFSIZ) && (lcldviname[ctr] != '\0') && (lcldviname[ctr] != ' '); ctr++) ; lcldviname[ctr] = '\0'; if ((dvif=fopen(lcldviname,"r"))==NULL) { eofdvifile = 1; /* meaningless but otherwise someone would forget to do it when modifying this code */ fprintf(stderr,"Sorry, cannot open %s for reading\n",lcldviname); fflush(stderr); exit(1); } else eofdvifile = 0; } /* return unsigned version of next byte in dvifile */ int getbyte() { register int c; if ((c = getc(dvif))==EOF) { eofdvifile = 1; return(0); } curloc++; return(c); } /* return signed version of next byte in dvifile */ int signedbyte() { register int c; if ((c = getc(dvif))==EOF) { eofdvifile = 1; return(0); } curloc++; if (c>127) return(c-256); return(c); } /* return unsigned value of next two bytes (high order first) */ int gettwobytes() { register int a,b; a = getc(dvif); eofdvifile = ((b = getc(dvif))==EOF); curloc += 2; return((a << 8) | b); } /* return signed value of next two bytes (high order first) */ int signedpair() { register int a,b; if ( (a = getc(dvif)) > 127) a -= 256; /* sign extend */ eofdvifile = ((b = getc(dvif))==EOF); curloc += 2; return((a << 8) | b); } /* return unsigned value of next three bytes */ int getthreebytes() { register int a,b,c; a = getc(dvif); b = getc(dvif); eofdvifile = ((c = getc(dvif))==EOF); curloc +=3; return((a << 16) | (b << 8) | c); } /* return signed value of next three bytes */ int signedtrio() { register int a,b,c; if ( (a = getc(dvif)) > 127) a -= 256; b = getc(dvif); eofdvifile = ((c = getc(dvif))==EOF); curloc +=3; return((a << 16) | (b << 8) | c); } /* return value of next four bytes */ int signedquad() { register int a,b,c,d; a = getc(dvif); b = getc(dvif); c = getc(dvif); eofdvifile = ((d = getc(dvif))==EOF); curloc += 4; return((a << 24) | (b << 16) | (c << 8) | d); } /* seek to byte n of file f: actually, assume f is for dvifile */ setdvipos(f,n) int f; /* not really, but we ignore it */ int n; { if (n>=0) { fseek(dvif,(long) n,0); eofdvifile = 0; } else { fseek(dvif, (long) n, 2); eofdvifile = 1; } } /* return current byte offset in file f (again, assume dvifile) */ int curdvipos(f) int f; { return((int) ftell(dvif)); } /* added routines along the same lines */ static FILE *lgpf; extern char lgpname[]; /* open the lgpfile, using the name in lgpname */ openlgpfile() { char lcllgpname[BUFSIZ]; /* for a local copy of the dvi name */ int ctr; strcpy(lcllgpname,lgpname); /* a local copy to play with */ /* the lgp name terminates on the first blank */ for(ctr = 0; (ctr < BUFSIZ) && (lcllgpname[ctr] != '\0') && (lcllgpname[ctr] != ' '); ctr++) ; lcllgpname[ctr] = '\0'; if((lgpf=fopen(lcllgpname,"w"))==NULL) { fprintf(stderr,"Sorry, cannot open %s for writing\n",lcllgpname); fflush(stderr); exit(1); } } /* output a byte to the lgp file */ outputbyte(b) int b; { char ch; putc((b&0377),lgpf); } /* ---and now, some routines borrowed and modified from TeX to handle reading pxl files--- */ /* * readbyte reads from the Pascal text file with iorec pointer filep * returning the byte read. This is modified from lineread in TeX. * If end-of-file is encountered, the funit field of *filep is set * appropriately. */ int readbyte(filep) struct iorec *filep; { register c; register FILE *iop; /* stdio-style FILE pointer */ iop = filep->fbuf; /* overflow when next char would go into buffer[lastlim] */ c = getc(iop); if (c<=0) filep->funit |= EOFF; /* we hit end-of-file */ return(c); } /* ** testeof(filep) ** ** Test whether or not the Pascal text file with iorec pointer filep ** has reached end-of-file (when the only I/O on it is done with ** lineread, above). ** We may have to read the next character and unget it to see if perhaps ** the end-of-file is next. */ bool testeof(filep) register struct iorec *filep; { register char c; register FILE *iop; /* stdio-style FILE pointer */ if (filep->funit & EOFF) return(TRUE); else { /* check to see if next is EOF */ iop = filep->fbuf; c = getc(iop); if (c<0) return(TRUE); else { ungetc(c,iop); return(FALSE); } } } /* and now, some routines to implement paths for file names. These routines are taken from Howard Trickey's port of dvitype (2/19/84). Howard adapted them from his port of TeX, ext.c */ #include "../../texpaths.h" /* defines default TEXFONTS path */ /* ---fixme--- use the TeX default here!!! */ char *fontpath; char *getenv(); /* * setpaths is called to set up the pointer fontpath * as follows: if the user's environment has a value for TEXFONTS * then use it; otherwise, use defaultfontpath. */ setpaths() { register char *envpath; if ((envpath = getenv("TEXFONTS")) != NULL) fontpath = envpath; else fontpath = defaultfontpath; } #define namelength 100 /* should agree with DISTdvi2lgp.ch */ extern char curname[],realnameoffile[]; /* these have size namelength */ /* * testaccess(amode,filepath) * * Test whether or not the file whose name is in the global curname * can be opened for reading (if mode=READACCESS) * or writing (if mode=WRITEACCESS). * * The filepath argument is one of the ...FILEPATH constants defined below. * If the filename given in curname does not begin with '/', we try * prepending all the ':'-separated areanames in the appropriate path to the * filename until access can be made, if it ever can. * * The realnameoffile global array will contain the name that yielded an * access success. */ /* note that corresponding constants are defined in DISTdvi2lgp.ch */ #define READACCESS 4 #define WRITEACCESS 2 #define NOFILEPATH 0 #define FONTFILEPATH 3 bool testaccess(amode,filepath) int amode,filepath; { register bool ok; register char *p; char *curpathplace; int f; switch(filepath) { case NOFILEPATH: curpathplace = NULL; break; case FONTFILEPATH: curpathplace = fontpath; break; } if (curname[0]=='/') /* file name has absolute path */ curpathplace = NULL; do { packrealnameoffile(&curpathplace); if (amode==READACCESS) /* use system call "access" to see if we could read it */ if (access(realnameoffile,READACCESS)==0) ok = TRUE; else ok = FALSE; else { /* WRITEACCESS: use creat to see if we could create it, but close the file again if we're OK, to let pc open it for real */ f = creat(realnameoffile,0666); if (f>=0) ok = TRUE; else ok = FALSE; if (ok) close(f); } } while (!ok && curpathplace != NULL); if (ok) { /* pad realnameoffile with blanks, as Pascal wants */ for (p = realnameoffile; *p != '\0'; p++) /* nothing: find end of string */ ; while (p < &(realnameoffile[namelength])) *p++ = ' '; } return (ok); } /* * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp, * followed by '/', followed by the characters in curname up until the * first blank there, and finally a '\0'. The cpp pointer is left pointing * at the next directory in the path. * But: if *cpp == NULL, then we are supposed to use curname as is. */ packrealnameoffile(cpp) char **cpp; { register char *p,*realname; realname = realnameoffile; if ((p = *cpp)!=NULL) { while ((*p != ':') && (*p != '\0')) { *realname++ = *p++; if (realname == &(realnameoffile[namelength-1])) break; } if (*p == '\0') *cpp = NULL; /* at end of path now */ else *cpp = p+1; /* else get past ':' */ *realname++ = '/'; /* separate the area from the name to follow */ } /* now append curname to realname... */ p = curname; while (*p != ' ') { if (realname >= &(realnameoffile[namelength-1])) { fprintf(stderr,"! Full file name is too long\n"); break; } *realname++ = *p++; } *realname = '\0'; } SHAR_EOF fi # end of overwriting check if test -f '4.2-Note' then echo shar: will not over-write existing file "'4.2-Note'" else cat << \SHAR_EOF > '4.2-Note' Make sure to cp 4.2Makefile Makefile before you begin SHAR_EOF fi # end of overwriting check if test -f '4.2Makefile' then echo shar: will not over-write existing file "'4.2Makefile'" else cat << \SHAR_EOF > '4.2Makefile' # # Makefile for Berkeley UNIX version of dvi2lgp, 4.2 bsd # # Note: The -J option on the 'pc' commands below is the only way to # 'pc' to use the -J option to the assembler (which makes it # do the sensible thing with branches). This flag is not # documented, which is correct, since 'pc' should really be # using it all of the time... (Grrr..) # # Note: dvi2lgp uses the ../../texpaths.h file defined for use by # TeX. It refers to the TEXFONTS environment variable to # decide where to look for font bit maps. # # Also, the local variable FIXOTHERS is set to ``pxp -O -f'' for 4.2 # since you are not going to use a hacked pc compiler that recognizes # the ``others:'' clause. You will need the new Berkeley pxp program # that has the -O option to transform case statements with such # clauses into code that doesn't use ``others:''. # We use ``pxp -O -f'' here instead of ``pxp -O'' because we # want to have expressions fully parenthesized to avoid a pxp bug. # If you are using a hacked pc compiler, then set FIXOTHERS # to ``cat''. # TEXPC=pc PCLIB= PFLAGS= -J -O CFLAGS = -O FIXOTHERS= pxp -O -f all: dvi2lgp # # Compile dvi2lgp # dvi2lgp: dvi2lgp.o lgpext.o ${TEXPC} $(PFLAGS) -o dvi2lgp dvi2lgp.o lgpext.o ${PCLIB} dvi2lgp.o: dvi2lgp.p $(TEXPC) $(PFLAGS) -c dvi2lgp.p dvi2lgp.p: dvi2lgp.web dvi2lgp.ch tangle dvi2lgp.web dvi2lgp.ch $(FIXOTHERS) dvi2lgp.p > dvi2lgp.temp mv dvi2lgp.temp dvi2lgp.p lgpext.o: lgpext.c lgpext.h ../../h00vars.h ../../texpaths.h # # clean: rm -f *.p *.o dvi2lgp.pool SHAR_EOF fi # end of overwriting check if test -f 'README' then echo shar: will not over-write existing file "'README'" else cat << \SHAR_EOF > 'README' Converts dvi files into a form that can be fed to the lgp with the lgp command. Expected usage would be tex foo dvi2lgp foo lgp foo.lgp Make sure that ./tex82/h00vars.h is in place before making dvi2lgp. If you are running 4.1bsd then run the shell script 4.1-setup. If you are running 4.2 bsd, run the shell script 4.2-setup. Do this before trying to make the program. This program was written at the University of Washington, Department of Computer Science. Contact Richard Furuta or Pierre MacKay if problems arise. SHAR_EOF fi # end of overwriting check if test -f 'dvi2lgp.ch' then echo shar: will not over-write existing file "'dvi2lgp.ch'" else cat << \SHAR_EOF > 'dvi2lgp.ch' % Copyright 1983 by Richard Furuta and Pierre MacKay. Copying without % fee is permitted provided that the copies are not made or distributed % for direct commercial advantage and credit to the source is given. % Altered versions of this program are not to be copied % or distributed without permission from the authors. % There are no restrictions on the use of this program. % % corresponds to DVI2LGP, version 2.2 and uses Tangle, version 2.5 % Richard Furuta, University of Washington % things that need to be attended to are flagged with --fixme-- % % [0] -- Changes to the preamble % @x \def\title{DVI2LGP} @y \def\title{DVI2LGP for Berkeley Unix} @z @x [0] -- continued -- weave only prints changes \pageno=\contentspagenumber \advance\pageno by 1 @y \pageno=\contentspagenumber \advance\pageno by 1 \let\maybe=\iffalse @z % [10] program header @x @ The binary input comes from |dvi_file|, and symbolic output can be written on \PASCAL's standard |output| file. The term |print| is used instead of |write| when this program writes on |output|, so that all such output can easily be redirected if desired. We use the terms |tprint| and |tprint_ln| for terminal output, in order to make any necessary change easier. The |term_in| and |term_out| files are defined later. @d print(#)==write(#) @d print_ln(#)==write_ln(#) @d tprint(#)==write(term_out,#) {|write| will work in most environments} @d tprint_ln(#)==write_ln(term_out,#) {|write_ln| likewise} @y @ The binary input comes from |dvi_file|, and symbolic output can be written on \PASCAL's standard |output| file. The term |print| is used instead of |write| when this program writes on |output|, so that all such output can easily be redirected if desired. We use the terms |tprint| and |tprint_ln| for terminal output, in order to make any necessary change easier. The |term_in| and |term_out| files are defined to be input and output for the moment since we can't quite figure out how to direct things to |stdout| in Unix in a reasonable way. @d print(#)==write(#) @d print_ln(#)==write_ln(#) @d term_in==input {the terminal as an input file (stdin)} @d term_out==output {the terminal as an output file (stdout)} @d tprint(#)==write(term_out,#) {|write| will work in most environments} @d tprint_ln(#)==write_ln(term_out,#) {|write_ln| likewise} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z @x --- continuing from [10] --- the program declaration program DVI2LGP(@!output); @y program DVI2LGP(@!input, @!output); @z @x --- continuing from [10] --- the #include directive var@?@<Globals in the outer block@>@/ @y var@?@<Globals in the outer block@>@/ @# @\@=#include "lgpext.h"@>@\ {declarations for external C procedures} @# @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z @x --- continuing from [10] --- initialize paths @<Set initial values@>@/ @y setpaths; {read environment, to find TEXFONTS, if there} @<Set initial values@>@/ @z % [11] -- including or excluding debugging code--may not actually change % but reproduced here for convenience. @x @ Some of this code is optional for use when debugging only; such material is enclosed between the delimiters |debug| and $|gubed|$. Other parts, delimited by |stat| and $|tats|$, are optionally included if statistics about \.{DVI2LGP}'s memory usage are desired. @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} @f debug==begin @f gubed==end @# @d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering usage statistics} @d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering usage statistics} @f stat==begin @f tats==end @y @ Some of this code is optional for use when debugging only; such material is enclosed between the delimiters |debug| and $|gubed|$. Other parts, delimited by |stat| and $|tats|$, are optionally included if statistics about \.{DVI2LGP}'s memory usage are desired. @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} @f debug==begin @f gubed==end @# @d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering usage statistics} @d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering usage statistics} @f stat==begin @f tats==end @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [12] compiler directives @x @ The \PASCAL\ compiler used to develop this system has ``compiler directives'' that can appear in comments whose first character is a dollar sign. In production versions of \.{DVI2LGP} these directives tell the compiler that @^system dependencies@> it is safe to avoid range checks and to leave out the extra code it inserts for the \PASCAL\ debugger's benefit, although interrupts will occur if there is arithmetic overflow. There is also an directive to ensure that the ``\&{extended addressing}'' feature in TOPS-20 \PASCAL\ is included. @<Compiler directives@>= @{@&$X+,C-,A+,D-@} {extended adr., no range check, catch overflow, no debug} debug @{@&$X+,C+,D+@}@+ gubed {but turn everything on when debugging} @y @ The \PASCAL\ compiler used to develop this system has ``compiler directives'' that can appear in comments whose first character is a dollar sign. The directives shown below specify full checking and inclusion of the \PASCAL\ debugger when \.{DVI2LGP} is being debugged, but they cause range checking and other redundant code to be eliminated when the production system is being generated. Arithmetic overflow will be detected in all cases. We use verbatim \PASCAL\ mode to make sure the case of the options is unchanged. @^Overflow in arithmetic@> @<Compiler directives@>= @=(*$C-*)@> {no range check} debug @=(*$C+*)@> @+ gubed {but turn everything back on when debugging} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [14] Increase name_length. Note that this definition is repeated in % lgpext.c and that the two should match. @x @!name_length=50; {a file name shouldn't be longer than this} @y @!name_length=100; {a file name shouldn't be longer than this} @z % [21] changing the case of names---yes or no @x @d system_case_of(#)==fold_upper(#) {leave out the adjustment for {\mc UNIX}} @y @d system_case_of(#)==xchr[#] {leave out the adjustment for {\mc UNIX}} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [33] definitions for binary files @x We shall stick to simple \PASCAL\ in this program, for reasons of clarity, even if such simplicity is sometimes unrealistic. @y Here with Berkeley {\mc UNIX}, it turns out that asking for a `|packed file of -128..127|' does what we want. We do have to convert values when read, but the external files look fine. @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % % [33] continued % @x @!byte_file=packed file of eight_bits; {files that contain binary data} @y @!byte_file=packed file of -128..127; {files that contain binary data} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [35] @x @p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|} begin reset(dvi_file,dvi_name); cur_loc:=0; end; @# procedure open_lgp_file; {prepares \.{LGP} file for output} begin rewrite(lgp_file,lgp_name); end; @y On Berkeley {\mc UNIX}, use external procedures for opening the |dvi_file| and |lgp_file|, so that we can do random reading and sequential writing without mucking with the pc runtime data structures. This also means that the |eof| tests for |dvi_file| need to be handled externally. This routine was borrowed from Howard Trickey's |dvip| program. We do use this opportunity, though, to define some file opening related constants although they are not used here (they get used later when the \.{PXL} files are opened). These definitions are repeated in lgpext.c and should be kept constant. @d read_access_mode=4 {``read'' mode for |test_access|} @d write_access_mode=2 {``write'' mode for |test_access|} @d no_file_path=0 {no path searching should be done} @d font_file_path=3 {path specifier for \.{PXL} files} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [37] reading bytes from dvi_file--routines borrowed from Trickey's dvip @x @ We shall use another set of simple functions to read the next byte or bytes from |dvi_file|. There are seven possibilities, each of which is treated as a separate function in order to minimize the overhead for subroutine calls. @^system dependencies@> @p function get_byte:integer; {returns the next byte, unsigned} var b:eight_bits; begin if eof(dvi_file) then get_byte:=0 else begin read(dvi_file,b); incr(cur_loc); get_byte:=b; end; end; @# function signed_byte:integer; {returns the next byte, signed} var b:eight_bits; begin read(dvi_file,b); incr(cur_loc); if b<128 then signed_byte:=b @+ else signed_byte:=b-256; end; @# function get_two_bytes:integer; {returns the next two bytes, unsigned} var a,@!b:eight_bits; begin read(dvi_file,a); read(dvi_file,b); cur_loc:=cur_loc+2; get_two_bytes:=a*256+b; end; @# function signed_pair:integer; {returns the next two bytes, signed} var a,@!b:eight_bits; begin read(dvi_file,a); read(dvi_file,b); cur_loc:=cur_loc+2; if a<128 then signed_pair:=a*256+b else signed_pair:=(a-256)*256+b; end; @# function get_three_bytes:integer; {returns the next three bytes, unsigned} var a,@!b,@!c:eight_bits; begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); cur_loc:=cur_loc+3; get_three_bytes:=(a*256+b)*256+c; end; @# function signed_trio:integer; {returns the next three bytes, signed} var a,@!b,@!c:eight_bits; begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); cur_loc:=cur_loc+3; if a<128 then signed_trio:=(a*256+b)*256+c else signed_trio:=((a-256)*256+b)*256+c; end; @# function signed_quad:integer; {returns the next four bytes, signed} var a,@!b,@!c,@!d:eight_bits; begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d); cur_loc:=cur_loc+4; if a<128 then signed_quad:=((a*256+b)*256+c)*256+d else signed_quad:=(((a-256)*256+b)*256+c)*256+d; end; @y @ In this module, the \.{.WEB} file defines some simple functions to get the next byte or bytes from the |dvi_file|. For {\mc UNIX} we, use external |@!get_byte|, etc., procedures for efficiency reasons. Consequently, we will use this module to declare the |eof_dvi_file| variable that will be set by those procedures. @<Globals...@>= @!eof_dvi_file:boolean; {|true| when \.{DVI} file has ended} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [38] Positioning within the dvi file @x Such routines are, of course, highly system dependent. They are implemented here in terms of two assumed system routines called |set_pos| and |cur_pos|. The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is negative or larger than the total number of items in |f|; in the latter case, |set_pos(f,n)| moves to the end of file |f|. The call |cur_pos(f)| gives the total number of items in |f|, if |eof(f)| is true; we use |cur_pos| only in such a situation. @p function dvi_length:integer; begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file); end; @# procedure move_to_byte(n:integer); begin set_pos(dvi_file,n); cur_loc:=n; end; @y Such routines are, of course, highly system dependent. They are implemented in Berkeley Unix with four assumed system routines. The two called |set_pos| and |cur_pos| are typical. The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is negative or larger than the total number of items in |f|; in the latter case, |set_pos(f,n)| moves to the end of file |f|. The call |cur_pos(f)| gives the total number of items in |f|, if |eof(f)| is true; we use |cur_pos| only in such a situation. Berkeley Unix also defines |cur_dvi_pos| and |set_dvi_pos| because the |dvi_file| routines are defined using external C procedures which are not happy when given a Pascal file descriptor. @p function dvi_length:integer; begin set_dvi_pos(dvi_file,-1); dvi_length:=cur_dvi_pos(dvi_file); end; @# procedure move_to_byte(n:integer); begin set_dvi_pos(dvi_file,n); cur_loc:=n; end; @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [39] converting internal to external representations for byte files @x @ The attempt to output |eight_bits| bytes is virtually certain to require some sort of system-dependent code. What we show here is far too simple-minded to work on any computer system we have access to, and it is intended primarily to provide a place for the appropriate routine in a \.{WEB} change file. @p procedure output_byte(b:integer); begin if b<0 then b:=b+@'400; {for an |eight_bits| value, remove sign} write(lgp_file,b); end; @^system dependencies@> @y @ The attempt to output |eight_bits| bytes is virtually certain to require some system dependent code. For Berkeley {\mc UNIX}, the numbers output to |lgp_file| must be in the range |-128..127|. If we subtract 256 from numbers greater than 127, all will work out fine. This is all done in an external procedure for performance reasons @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [43] Storage for pxl font file names @x @ When we are inputting from \.{PXL} files, it is usually preferable to read four bytes at a time. The input goes into global variables |b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3| the fourth. We also need |cur_name|, a string variable that will be set to the current \.{PXL} font file name before each of the elements in the |pxl_files| array is opened or |reset|. @<Glob...@>= @!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once} @!cur_name:packed array[1..name_length] of char; {external name, with no lower case letters} @y @ When we are inputting from \.{PXL} files, it is usually preferable to read four bytes at a time. The input goes into global variables |b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3| the fourth. We also need |cur_name|, a string variable that will be set to the current \.{PXL} font file name before each of the elements in the |pxl_files| array is opened or |reset|. Under UNIX, we also have a |real_name_of_file| string, that gets set by the external |test_access| procedure after path searching. @<Glob...@>= @!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once} @!cur_name,@!real_name_of_file:packed array[1..name_length] of char; {external name} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [44] reading bytes from the input pxl file @x @p procedure read_pxl_bytes; begin read(pxl_files[active_font],b0); read(pxl_files[active_font],b1); read(pxl_files[active_font],b2); read(pxl_files[active_font],b3); end; @y @p procedure read_pxl_bytes; var byte: -128..127; begin byte:=readbyte(pxl_files[active_font]); if byte<0 then b0:=byte+256 else b0:=byte; byte:=readbyte(pxl_files[active_font]); if byte<0 then b1:=byte+256 else b1:=byte; byte:=readbyte(pxl_files[active_font]); if byte<0 then b2:=byte+256 else b2:=byte; byte:=readbyte(pxl_files[active_font]); if byte<0 then b3:=byte+256 else b3:=byte; end; @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [53] use our eof tester, not Pascal @x get_pxl_word(pxl_id); if not eof(pxl_files[active_font]) then goto 9997; @y get_pxl_word(pxl_id); if not test_eof(pxl_files[active_font]) then goto 9997; @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [64] font area definition @x The string |cur_name| is supposed to be set to the external name of the \.{PXL} or \.{VRL} file for the current font. This usually means that we need to prepend the name of the default directory, and to append the four digits which indicate the magnification applied to this font when it was created, and to add the suffix `\.{PXL}' or \.{VRL} after these. In most environments, we should also change lower case letters to upper case, since |cur_name| is a \PASCAL\ string. @y The string |cur_name| is supposed to be set to the external name of the \.{PXL} file for the current font. This usually means that we need to prepend the name of the default directory, and to append the four digits which indicate the magnification applied to this font when it was created, and to add the suffix `\.{PXL}' after these. In most environments, we should also change lower case letters to upper case, since |cur_name| is a \PASCAL\ string. However, in Berkeley {\mc UNIX}, we retain the lower case letters since case is significant in file names. @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % % [64] continued--font area definition % @x @d default_directory_name=='TEXFONTS:' {change this to the correct name} @d default_directory_name_length=9 {change this to the correct length} @d extension_length=8 {length of the string (e.g.) ``\.{.1200PXL}''} @<Glob...@>= @!default_directory:packed array[1..default_directory_name_length] of char; @y However, most of this is not needed for Berkeley Unix since we are going to run down an externally defined path. The definition for this path is found in the TEXFONTS environment variable. If none is given, a default is used, defined in the ``texpaths.h'' file which is included by lgpext.c. @d extension_length=8 {length of the string (e.g.) ``\.{.1200PXL}''} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [65] initialize the variable we just eliminated @x @ @<Set init...@>= default_directory:=default_directory_name; @^system dependencies@> @y @ Nothing happens here, either, since there is nothing to initialize. This module is kept to preserve numbering. @z % [66] The work associated with opening the font file. @x @ Now for the real work of opening the font file. @<Move font name...@>= for k:=1 to name_length do cur_name[k]:=' '; p:= names[font_name[this_font]]; n:=names[font_name[this_font]+1]; if p=0 then begin for k:=1 to default_directory_name_length do cur_name[k]:=default_directory[k]; r:=default_directory_name_length; end else r:=0; for k:=font_name[this_font]+2 to font_name[this_font+1]-1 do begin incr(r); if r+4>name_length then abort('DVI2LGP capacity exceeded (max font name length=', name_length:1,')!'); @.DVI2LGP capacity exceeded...@> if (names[k]>="a")and(names[k]<="z") then cur_name[r]:=system_case_of(names[k]) {a harmless do-nothing in {\mc UNIX}} else cur_name[r]:=xchr[names[k]]; end; fm:=font_mag[this_font]; cur_name[r+1]:='.'; if fm > 999 then begin a:=(fm div 1000)+ord('0'); cur_name[r+2]:=xchr[a]; fm:=fm mod 1000; end else decr(r); {for diminished fonts such as \.{999PXL}} a:=(fm div 100)+ord('0'); cur_name[r+3]:=xchr[a]; fm:=fm mod 100; a:=(fm div 10)+ord('0'); cur_name[r+4]:=xchr[a]; fm:=fm mod 10; a:=fm+ord('0'); cur_name[r+5]:=xchr[a]; cur_name[r+6]:=system_case_of("p"); cur_name[r+7]:=system_case_of("x"); cur_name[r+8]:=system_case_of("l"); if not take_defaults then begin if in_postamble then print(' == ') else if out_mode>errors_only then print(' requeue '); @.requeue (a previously queued font)@> if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then begin for k:=1 to r+extension_length do print(cur_name[k]);print_ln(' '); end; end; reset(pxl_files[p_f^.cache_number],cur_name); p_f^.font_ptr:=this_font @^system dependencies@> @y @ Now for the real work of opening the font file. @<Move font name...@>= for k:=1 to name_length do cur_name[k]:=' '; p:= names[font_name[this_font]]; n:=names[font_name[this_font]+1]; r:=0; for k:=font_name[this_font]+2 to font_name[this_font+1]-1 do begin incr(r); if r+4>name_length then abort('DVI2LGP capacity exceeded (max font name length=', name_length:1,')!'); @.DVI2LGP capacity exceeded...@> if (names[k]>="a")and(names[k]<="z") then cur_name[r]:=system_case_of(names[k]) {a harmless do-nothing in {\mc UNIX}} else cur_name[r]:=xchr[names[k]]; end; fm:=font_mag[this_font]; cur_name[r+1]:='.'; if fm > 999 then begin a:=(fm div 1000)+ord('0'); cur_name[r+2]:=xchr[a]; fm:=fm mod 1000; end else decr(r); {for diminished fonts such as \.{999PXL}} a:=(fm div 100)+ord('0'); cur_name[r+3]:=xchr[a]; fm:=fm mod 100; a:=(fm div 10)+ord('0'); cur_name[r+4]:=xchr[a]; fm:=fm mod 10; a:=fm+ord('0'); cur_name[r+5]:=xchr[a]; cur_name[r+6]:=system_case_of("p"); cur_name[r+7]:=system_case_of("x"); cur_name[r+8]:=system_case_of("l"); if not take_defaults then begin if in_postamble then print(' == ') else if out_mode>errors_only then print(' requeue '); @.requeue (a previously queued font)@> if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then begin for k:=1 to r+extension_length do print(cur_name[k]);print_ln(' '); end; end; if test_access(read_access_mode,font_file_path) then reset(pxl_files[p_f^.cache_number],real_name_of_file) else begin k:=1; print('Problem: font '); while((k <= name_length) and (cur_name[k] <> ' ')) do begin print(cur_name[k]); incr(k); end; print_ln(' cannot be found'); abort('---aborting'); end; p_f^.font_ptr:=this_font @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [70] printing an integer in octal @x @ The |check_sum| in |tfm_file|s is recorded in a \.{PL} property list in octal notation, and it is helpful to be able to check errors against the information provided by \.{TFtoPL}, so if any |check_sum| discrepancies appear, we print the information in octal notation. This is not the same routine as the one in \.{PLtoTF} because, in this program, the value to be printed may already have been altered by sign-extension. The value of |kill_sign| is legitimate in \.{WEB} but may cause trouble in some environments. @^system dependencies@> @d kill_sign==(-@'17777777777-1) {so that we can have an unsigned value} @p procedure print_octal(@!a:integer); {outputs an integer in octal} var @!s:integer; {surrogate sign-bit} @!i:integer; {what we are working on} @!j:0..11; {one more than the number of digits of output} @!dig:array[0..10] of "0".."7"; {eleven digit maximum} begin print(' O '); {specify octal format} s:=0; if a < 0 then begin i:=a + kill_sign; s:= 2; end else i:=a; for j:=0 to 10 do dig[j]:="0"; j:=0; while (i>0)or(j=0) do begin dig[j]:=(i mod 8) + "0"; i:=i div 8; incr(j); end; if s>0 then begin dig[10]:=dig[10] + s; {restore lost ``sign-bit'' if any} j:=11; {picks up effects such as \.{O} 20000012345} end; repeat decr(j); print(xchr[dig[j]]); until j=0; end; @y @ The |check_sum| in |tfm_file|s is recorded in a \.{PL} property list in octal notation, and it is helpful to be able to check errors against the information provided by \.{TFtoPL}, so if any |check_sum| discrepancies appear, we print the information in octal notation. In this routine, we just use Berkeley {\mc UNIX}'s extension which allows printing of an octal number. @d oct_output_digits=11 @p procedure print_octal(@!a:integer); {outputs an integer in octal} begin print(' O '); {specify octal format} print(a:oct_output_digits oct); end; @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [75] another use of our private eof function @x if eof(pxl_files[active_font]) then begin @y if test_eof(pxl_files[active_font]) then begin @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [82] terminal input and output files @x @!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} @y @!buffer:array[0..terminal_line_length] of ascii_code; @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [83] updating the terminal @x @d update_terminal == break(term_out) {empty the terminal output buffer} @y @d update_terminal == flush(term_out) {empty the terminal output buffer} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [84] getting lines from the terminal @x @ During the dialog, \.{DVI2LGP} will treat the first blank space in a line as the end of that line. Therefore |input_ln| makes sure that there is always at least one blank space in |buffer|. @^system dependencies@> @p procedure input_ln; {inputs a line from the terminal} var k:0..terminal_line_length; begin update_terminal; reset(term_in); if eoln(term_in) then read_ln(term_in); k:=0; while (k<terminal_line_length)and not eoln(term_in) do begin buffer[k]:=xord[term_in^]; incr(k); get(term_in); end; buffer[k]:=" "; end; @y @ During the dialog, \.{DVI2LGP} 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|. |first_get| is true initially. @^system dependencies@> @p procedure input_ln; {inputs a line from the terminal} var k:0..terminal_line_length; begin update_terminal; if first_get then first_get:=false else begin if eoln(term_in) then read_ln(term_in) end; k:=0; while (k<terminal_line_length)and not eoln(term_in) do begin buffer[k]:=xord[term_in^]; incr(k); get(term_in); end; buffer[k]:=" "; end; @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [87] top level for user dialog @x @ The selected options are put into global variables by the |dialog| procedure, which is called just as \.{DVI2LGP} begins. @^system dependencies@> @p procedure dialog; label 0,1,2,3,4,5; var k:integer; {loop variable} ctr, tctr, lctr, dctr: integer; {loop variables for getting file names} begin rewrite(term_out); {prepare the terminal for output} @y @ The selected options are put into global variables by the |dialog| procedure, which is called just as \.{DVI2LGP} begins. @^system dependencies@> @p procedure dialog; label 0,1,2,3,4,5; var k:integer; {loop variable} ctr, tctr, lctr, dctr: integer; {loop variables for getting file names} begin @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [94] parsing the file name argument @x @d area_marker==':' {or whatever your system uses} @d alt_marker=='>' {change this to things like the $\rangle$ directory bracket} @d ext_marker=='.' {the normal character for file-name extensions} @y @d area_marker=='/' {or whatever your system uses} @d alt_marker=='/' {change this to things like the $\rangle$ directory bracket} @d ext_marker=='.' {the normal character for file-name extensions} @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [124] change for eof(dvi_file) @x if eof(dvi_file) then bad_dvi('the file ended prematurely!'); @y if eof_dvi_file then bad_dvi('the file ended prematurely!'); @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [148] change for eof(dvi_file) @x begin if eof(dvi_file) then bad_dvi('the file ended prematurely!'); @y begin if eof_dvi_file then bad_dvi('the file ended prematurely!'); @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z % [156] change for eof(dvi_file) @x 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, @y 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, @^system dependencies@>@^changes for Berkeley {\mc UNIX}@> @z SHAR_EOF fi # end of overwriting check if test -f '4.1-setup' then echo shar: will not over-write existing file "'4.1-setup'" else cat << \SHAR_EOF > '4.1-setup' # /bin/sh # set the symbolics area up for 4.1bsd cp 4.1Makefile Makefile SHAR_EOF fi # end of overwriting check if test -f '4.1Makefile' then echo shar: will not over-write existing file "'4.1Makefile'" else cat << \SHAR_EOF > '4.1Makefile' # # Makefile for Berkeley UNIX version of dvi2lgp, 4.1 bsd # # Note: The -J option on the 'pc' commands below is the only way to # 'pc' to use the -J option to the assembler (which makes it # do the sensible thing with branches). This flag is not # documented, which is correct, since 'pc' should really be # using it all of the time... (Grrr..) # # Note: dvi2lgp uses the ../../texpaths.h file defined for use by # TeX. It refers to the TEXFONTS environment variable to # decide where to look for font bit maps. # TEXPC=texpc PCLIB=../../=4.1support/pclib.a PFLAGS= -J -O CFLAGS = -O all: dvi2lgp # # Compile dvi2lgp # dvi2lgp: dvi2lgp.o lgpext.o ${TEXPC} $(PFLAGS) -o dvi2lgp dvi2lgp.o lgpext.o ${PCLIB} dvi2lgp.o: dvi2lgp.p $(TEXPC) $(PFLAGS) -c dvi2lgp.p dvi2lgp.p: dvi2lgp.web dvi2lgp.ch tangle dvi2lgp.web dvi2lgp.ch lgpext.o: lgpext.c lgpext.h ../../h00vars.h ../../texpaths.h # # clean: rm -f *.p *.o dvi2lgp.pool SHAR_EOF fi # end of overwriting check cd .. # End of shell archive exit 0