diff options
Diffstat (limited to 'Build/source/texk/web2c/dvitomp.web')
-rw-r--r-- | Build/source/texk/web2c/dvitomp.web | 1680 |
1 files changed, 0 insertions, 1680 deletions
diff --git a/Build/source/texk/web2c/dvitomp.web b/Build/source/texk/web2c/dvitomp.web deleted file mode 100644 index da6ce104e44..00000000000 --- a/Build/source/texk/web2c/dvitomp.web +++ /dev/null @@ -1,1680 +0,0 @@ -% $Id: dvitomp.web,v 1.3 2005/04/28 06:45:21 taco Exp $ -% DVItoMP, by John Hobby. Public domain. - -% Version 0 was prepared (March 1990). -% Version 0.1 implemented virtual fonts. (May 1990) -% Version 0.30 outputs setbounds statments, ignores rules 1sp wide (May 1991) -% Version 0.60 outputs rules as penstrokes with butt ends -% Version 0.62 makes the output more robust when used in a macro definition -% Version 0.63 new version number only (Unix change file has // path searching) -% Version 0.632 outputs a setbounds path even if no 1sp vrule appears (Jan 1997) -% Version 0.64 avoids outputting wrong fonts, improves error handling (Jan 1998) - -% Although considerable effort has been expended to make the DVItoMP program -% correct and reliable, no warranty is implied; the author disclaims any -% obligation or liability for damages, including but not limited to -% special, indirect, or consequential damages arising out of or in -% connection with the use or performance of this software. - -% This program is loosely based on DVItype Version 3.0 -% It converts a DVI file into a sequence of MetaPost picture expressions. - -% TeX is a trademark of the American Mathematical Society. - -% Here is TeX material that gets inserted after \input webmac -\def\hang{\hangindent 3em\indent\ignorespaces} -\font\ninerm=cmr9 -\font\sc=cmcsc10 -\let\mc=\ninerm % medium caps for names like SAIL -\def\MP{MetaPost} -\def\LaTeX{{\rm L\kern-.36em\raise.3ex\hbox{\sc a}\kern-.15em - T\kern-.1667em\lower.7ex\hbox{E}\kern-.125emX}} -\def\PASCAL{Pascal} - -\def\(#1){} % this is used to make section names sort themselves better -\def\9#1{} % this is used for sort keys in the index - -\def\title{DVI$\,$\lowercase{to}MP} -\def\contentspagenumber{1} -\def\topofcontents{\null - \def\titlepage{F} % include headline on the contents page - \def\rheader{\mainfont\hfil \contentspagenumber} - \vfill - \centerline{\titlefont The {\ttitlefont DVItoMP} processor} - \vskip 15pt - \centerline{(Version 0.64, January 1998)} - \vfill} -\pageno=\contentspagenumber \advance\pageno by 1 - -@* Introduction. -The \.{DVItoMP} program reads binary device-independent (``\.{DVI}'') -files that are produced by document compilers such as \TeX, and converts them -into a symbolic form understood by \MP. It is loosely based on the \.{DVItype} -utility program that produces a more faithful symbolic form of a \.{DVI} file. - -The output file is a sequence of \MP\ picture expressions, one for every page -in the \.{DVI} file. It makes no difference to \.{DVItoMP} where the \.{DVI} -file comes from, but it is intended to process the result of running \TeX\ -or \LaTeX\ on the output of \.{MPtoTEX}. Such a \.{DVI} file will contain -one page for every \.{btex}$\ldots$\.{etex} block in the original input. -Processing in with \.{DVItoMP} creates a corresponding sequence of \MP\ picture -expressions for use as an auxiliary input file. Since \MP\ expects such files -to have the extension \.{.MPX}, the output of \.{DVItoMP} is sometimes called -an ``\.{MPX}'' file. - -The |banner| string defined here should be changed whenever \.{DVItoMP} -gets modified. - -@d banner=='% Written by DVItoMP, Version 1.003' - {the first line of the output file} - -@ This program is written in standard \PASCAL, except where it is necessary -to use extensions; for example, \.{DVItoMP} must read files whose names -are dynamically specified, and that would be impossible in pure \PASCAL. -All places where nonstandard constructions are used have been listed in -the index under ``system dependencies.'' -@!@^system dependencies@> - -Many \.{DVI}-reading programs need the ability to move to a random place -in a binary file. \.{DVItoMP} does not need to do this, but it does use -a default |case| as in \.{TANGLE}, \.{WEAVE}, etc. - -@ The binary input comes from |dvi_file|, and the symbolic output goes to -|mpx_file|. \PASCAL's standard |output| file is used only to print an error -message if the \.{DVI} file is bad. The term |print| is used instead of -|write| when this program writes on |mpx_file| just in case this helps -some installations deal with system dependencies. -@^system dependencies@> - -@d print(#)==write(mpx_file,#) -@d print_ln(#)==write_ln(mpx_file,#) -@d err_print(#)==write(#) -@d err_print_ln(#)==write_ln(#) - -@p program DVI_to_MP(@!dvi_file,@!mpx_file,@!output); -label @<Labels in the outer block@>@/ -const @<Constants in the outer block@>@/ -type @<Types in the outer block@>@/ -var @<Globals in the outer block@>@/ -procedure initialize; {this procedure gets things started properly} - var i:integer; {loop index for initializations} - begin @<Set initial values@>@/ - end; - -@ If the program has to stop prematurely, it goes to the -`|final_end|'. Another label, |done|, is used when stopping normally. - -@d final_end=9999 {label for the end of it all} -@d done=30 {go here when finished with a subtask} - -@<Labels...@>=final_end,done; - -@ The following parameters can be changed at compile time to extend or -reduce \.{DVItoMP}'s capacity. - -@<Constants...@>= -@!max_fonts=1000; {maximum number of distinct fonts per \.{DVI} file} -@!max_fnums=3000; {maximum number of fonts plus fonts local to virtual fonts} -@!max_widths=256*max_fonts; {maximum number of different characters among all fonts} -@!virtual_space=1000000; - {maximum total bytes of typesetting commands for virtual fonts} -@!line_length=79; {maximum output line length (must be at least 60)} -@!stack_size=100; {\.{DVI} files shouldn't |push| beyond this depth} -@!name_size=100000; {total length of all font file names} -@!name_length=250; {a file name shouldn't be longer than this} - -@ There is one more parameter that is a little harder to change because -it is of type |real|. - -@d font_tolerance==0.00001 - {font sizes should match to within this multiple of $2^{20}$ \.{DVI} units} - -@ 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} - -@ A global variable |history| keeps track of what type of errors have -occurred with the hope that that \MP\ can be warned of any problems. - -@d spotless=0 {|history| value when no problems have been found} -@d cksum_trouble=1 {|history| value there have been font checksum mismatches} -@d warning_given=2 {|history| value after a recoverable error} -@d fatal_error=3 {|history| value if processing had to be aborted} - -@<Glob...@>= -history:spotless..fatal_error; - -@ @<Set init...@>= -history:=spotless; - -@ If the \.{DVI} file is badly malformed, the whole process must be aborted; -\.{DVItoMP} 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 \.{DVItoMP}. -@^system dependencies@> - -@d abort(#)==begin err_print_ln('DVItoMP abort: ',#); - history:=fatal_error; jump_out; - end -@d bad_dvi(#)==abort('Bad DVI file: ',#,'!') -@.Bad DVI file@> -@d warn(#)==begin err_print_ln('DVItoMP warning: ',#); - history:=warning_given; - end - -@p procedure jump_out; -begin goto final_end; -end; - -@* The character set. -Like all programs written with the \.{WEB} system, \.{DVItoMP} can be -used with any character set. But it uses ASCII code internally, because -the programming for portable input-output is easier when a fixed internal -code is used, and because \.{DVI} files use ASCII code for file names. - -The next few sections of \.{DVItoMP} have therefore been copied from the -analogous ones in the \.{WEB} system routines. They have been considerably -simplified, since \.{DVItoMP} need not deal with the controversial -ASCII codes less than @'40 or greater than @'176. -If such codes appear in the font names, -they will be printed as question marks. - -@<Types...@>= -@!ASCII_code=" ".."~"; {a subrange of the integers} - -@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit -character sets were common, so it did not make provision for lower case -letters. Nowadays, of course, we need to deal with both upper and lower case -alphabets in a convenient way, especially in a program like \.{DVItoMP}. -So we shall assume that the \PASCAL\ system being used for \.{DVItoMP} -has a character set containing at least the standard visible characters -of ASCII code (|"!"| through |"~"|). - -Some \PASCAL\ compilers use the original name |char| for the data type -associated with the characters in text files, while other \PASCAL s -consider |char| to be a 64-element subrange of a larger data type that has -some other name. In order to accommodate this difference, we shall use -the name |text_char| to stand for the data type of the characters in the -output file. We shall also assume that |text_char| consists of -the elements |chr(first_text_char)| through |chr(last_text_char)|, -inclusive. The following definitions should be adjusted if necessary. -@^system dependencies@> - -@d text_char == char {the data type of characters in text files} -@d first_text_char=0 {ordinal number of the smallest element of |text_char|} -@d last_text_char=127 {ordinal number of the largest element of |text_char|} - -@<Types...@>= -@!text_file=packed file of text_char; - -@ The \.{DVItoMP} processor converts from ASCII code to -the user's external character set by means of an array |xchr| -that is analogous to \PASCAL's |chr| function. - -@<Globals...@>= -@!mpx_file:text_file; {destination for printed output} -@!xchr: array [0..255] of text_char; - {specifies conversion of output characters} - -@ To prepare the |mpx_file| for output, we |rewrite| it. -@^system dependencies@> - -@p procedure open_mpx_file; {prepares to write text on |mpx_file|} -begin rewrite(mpx_file); -end; - -@ 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]:='?'; - -@* Device-independent file format. -The format of \.{DVI} files is described in many places including -\.{dvitype.web} and Volume~B of D.~E. Knuth's {\sl Computers and Typesetting}. -This program refers to the following command codes. - -@d id_byte=2 {identifies the kind of \.{DVI} files described here} -@# -@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 - -@* Input from binary files. -We have seen that a \.{DVI} file is a sequence of 8-bit bytes. The bytes -appear physically in what is called a `|packed file of 0..255|' -in \PASCAL\ lingo. - -Packing is system dependent, and many \PASCAL\ systems fail to implement -such files in a sensible way (at least, from the viewpoint of producing -good production software). For example, some systems treat all -byte-oriented files as text, looking for end-of-line marks and such -things. Therefore some system-dependent code is often needed to deal with -binary files, even though most of the program in this section of -\.{DVItoMP} 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 two binary file variables: |dvi_file| is the main -input file that we are translating into symbolic form, and |tfm_file| is -the current font metric file from which character-width information is -being read. It is convenient to have a throw-away variable for function -results when reading parts of the files that are being skipped. - -@<Glob...@>= -@!dvi_file:byte_file; {the input file} -@!tfm_file:byte_file; {a font metric file} -@!vf_file:byte_file; {a virtual font file} -@!down_the_drain:integer; {a ``write-only'' variable} - -@ To prepare these files for input, we |reset| them. An extension of -\PASCAL\ is needed in the case of |tfm_file|, since we want to associate -it with external files whose names are specified dynamically (i.e., not -known at compile time). The following code 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, these routines assume that no file named |s| is accessible. -@^system dependencies@> - -@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|} -begin reset(dvi_file); -if eof(dvi_file) then abort('DVI file not found'); -end; -@# -function open_tfm_file:boolean; {prepares to read packed bytes in |tfm_file|} -begin reset(tfm_file,cur_name); -open_tfm_file:=(not eof(tfm_file)); -end; -@# -function open_vf_file:boolean; {prepares to read packed bytes in |vf_file|} -begin reset(vf_file,cur_name); -open_vf_file:=(not eof(vf_file)); -end; - -@ If you looked carefully at the preceding code, you probably asked, -``What is |cur_name|?'' Good question. It's a global -variable: |cur_name| is a string variable that will be set to the -current font metric file name before |open_tfm_file| is called. - -@<Glob...@>= -@!cur_name:packed array[1..name_length] of char; {external name, - with no lower case letters} - -@ It turns out to be convenient to read four bytes at a time, when we are -inputting from \.{TFM} files. The input goes into global variables -|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3| -the fourth. - -@<Glob...@>= -@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once} - -@ The |read_tfm_word| procedure sets |b0| through |b3| to the next -four bytes in the current \.{TFM} file. -@^system dependencies@> - -@p procedure read_tfm_word; -begin read(tfm_file,b0); read(tfm_file,b1); -read(tfm_file,b2); read(tfm_file,b3); -end; - -@ Input can come from from three different sources depending on the settings -of global variables. When |vf_reading| is true, we read from the \.{VF} file. -Otherwise, input can either come directly from |dvi_file| or from a buffer -|cmd_buf|. The latter case applies whenever |buf_ptr<virtual_space|. - -@<Glob...@>= -@!vf_reading:boolean; {should input come from |vf_file|?} -@!cmd_buf:packed array [0..virtual_space] of quarter_word; - {commands for virtual characters} -@!buf_ptr:0..virtual_space; {|cmd_buf| index for the next byte} - -@ @<Set init...@>= -vf_reading:=false; buf_ptr:=virtual_space; - -@ It is probably not critical that |cmd_buf| be packed as efficiently as possible, -but we define a new type just in case it is necessary to for |cmd_buf| entries -to be in the range |-128..127|. -@^system dependencies@> - -@d qi(#)==# {convert from |eight_bits| to |quarter_word|} -@d qo(#)==# {convert from |quarter_word| to |eight_bits|} - -@<Types...@>= -quarter_word=0..255; {a one byte quantity as stored in |cmd_buf|} - -@ We shall use a set of simple functions to read the next byte or bytes from the -current input source. There are seven possibilities, each of which is treated -as a separate function in order to minimize the overhead for subroutine calls. - -@p function get_byte:integer; {returns the next byte, unsigned} -var b:eight_bits; -begin @<Read one byte into |b|@>; -get_byte:=b; -end; -@# -function signed_byte:integer; {returns the next byte, signed} -var b:eight_bits; -begin @<Read one byte into |b|@>; -if b<128 then signed_byte:=b @+ else signed_byte:=b-256; -end; -@# -function get_two_bytes:integer; {returns the next two bytes, unsigned} -var a,@!b:eight_bits; -begin a:=0;b:=0; {for compiler warnings} -@<Read two bytes into |a| and |b|@>; -get_two_bytes:=a*256+b; -end; -@# -function signed_pair:integer; {returns the next two bytes, signed} -var a,@!b:eight_bits; -begin a:=0;b:=0; {for compiler warnings} -@<Read two bytes into |a| and |b|@>; -if a<128 then signed_pair:=a*256+b -else signed_pair:=(a-256)*256+b; -end; -@# -function get_three_bytes:integer; {returns the next three bytes, unsigned} -var a,@!b,@!c:eight_bits; -begin a:=0;b:=0;c:=0; {for compiler warnings} -@<Read three bytes into |a|, |b|, and~|c|@>; -get_three_bytes:=(a*256+b)*256+c; -end; -@# -function signed_trio:integer; {returns the next three bytes, signed} -var a,@!b,@!c:eight_bits; -begin a:=0;b:=0;c:=0; {for compiler warnings} -@<Read three bytes into |a|, |b|, and~|c|@>; -if a<128 then signed_trio:=(a*256+b)*256+c -else signed_trio:=((a-256)*256+b)*256+c; -end; -@# -function signed_quad:integer; {returns the next four bytes, signed} -var a,@!b,@!c,@!d:eight_bits; -begin a:=0;b:=0;c:=0;d:=0; {for compiler warnings} -@<Read four bytes into |a|, |b|, |c|, and~|d|@>; -if a<128 then signed_quad:=((a*256+b)*256+c)*256+d -else signed_quad:=(((a-256)*256+b)*256+c)*256+d; -end; - -@ @<Read one byte into |b|@>= -if vf_reading then read(vf_file,b) -else if buf_ptr=virtual_space then read(dvi_file,b) -else begin b:=qo(cmd_buf[buf_ptr]); - incr(buf_ptr); - end - -@ @<Read two bytes into |a| and |b|@>= -if vf_reading then - begin read(vf_file,a); read(vf_file,b); - end -else if buf_ptr=virtual_space then - begin read(dvi_file,a); read(dvi_file,b); - end -else if buf_ptr+2>n_cmds then - abort('Error detected while interpreting a virtual font') -@.Error detected while...@> -else begin a:=qo(cmd_buf[buf_ptr]); - b:=qo(cmd_buf[buf_ptr+1]); - buf_ptr:=buf_ptr+2; - end - -@ @<Read three bytes into |a|, |b|, and~|c|@>= -if vf_reading then - begin read(vf_file,a); read(vf_file,b); read(vf_file,c); - end -else if buf_ptr=virtual_space then - begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); - end -else if buf_ptr+3>n_cmds then - abort('Error detected while interpreting a virtual font') -@.Error detected while...@> -else begin a:=qo(cmd_buf[buf_ptr]); - b:=qo(cmd_buf[buf_ptr+1]); - c:=qo(cmd_buf[buf_ptr+2]); - buf_ptr:=buf_ptr+3; - end - -@ @<Read four bytes into |a|, |b|, |c|, and~|d|@>= -if vf_reading then - begin read(vf_file,a); read(vf_file,b); read(vf_file,c); read(vf_file,d); - end -else if buf_ptr=virtual_space then - begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d); - end -else if buf_ptr+4>n_cmds then - abort('Error detected while interpreting a virtual font') -@.Error detected while...@> -else begin a:=qo(cmd_buf[buf_ptr]); - b:=qo(cmd_buf[buf_ptr+1]); - c:=qo(cmd_buf[buf_ptr+2]); - d:=qo(cmd_buf[buf_ptr+3]); - buf_ptr:=buf_ptr+4; - end - -@* Data structures for fonts. -\.{DVI} file format does not include information about character widths, since -that would tend to make the files a lot longer. But a program that reads -a \.{DVI} file is supposed to know the widths of the characters that appear -in \\{set\_char} commands. Therefore \.{DVItoMP} looks at the font metric -(\.{TFM}) files for the fonts that are involved. -@.TFM {\rm files}@> - -@ For purposes of this program, the only thing we need to know about a -given character |c| in a non-virtual font |f| is the width. For the font as -a whole, all we need is the symbolic name to use in the \.{MPX} file. - -This information appears implicitly in the following data -structures. The current number of fonts defined is |nf|. Each such font has -an internal number |f|, where |0<=f<nf|. There is also an external number -that identifies the font in the \.{DVI} file. The correspondence is -maintained in arrays |font_num| and |internal_num| so that |font_num[i]| -is the external number for |f=internal_num[i]|. -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. The -legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive. -The \.{TFM} file can specify that some of these are invalid, but this doesn't -concern \.{DVItoMP} because it does not do extensive error checking. -The width of character~|c| in font~|f| is given by -|char_width(f)(c)=width[info_base[f]+c]|, and |info_ptr| is the -first unused position of the |width| array. - -If font~|f| is a virtual font, there is a list of \.{DVI} commands for each -character. These occupy consecutive positions in the |cmd_buf| array with -the commands for character~|c| starting at -|start_cmd(f)(c)=cmd_ptr[info_base[f]+c]| and ending just before -|start_cmd(f)(c+1)|. Font numbers used when interpreting these \.{DVI} -commands occupy positions |fbase[f]| through |ftop[f]-1| in the |font_num| -table and the |internal_num| array gives the corresponding internal font -numbers. If such an internal font number~|i| does not correspond to -some font occuring in the \.{DVI} file, then |font_num[i]| has not been -assigned a meaningful value; this is indicated by |local_only[i]=true|. - -If font~|f| is not virtual, then |fbase[f]=0| and |ftop[f]=0|. The |start_cmd| -values are ignored in this case. - -@d char_width_end(#)==#] -@d char_width(#)==width[info_base[#]+char_width_end -@d start_cmd(#)==cmd_ptr[info_base[#]+start_cmd_end -@d start_cmd_end(#)==#] - -@<Glob...@>= -@!font_num:array [0..max_fnums] of integer; {external font numbers} -@!internal_num:array [0..max_fnums] of integer; {internal font numbers} -@!local_only:array [0..max_fonts] of boolean; {|font_num| meaningless?} -@!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} -@!area_length:array [0..max_fonts] of integer; - {length of area part of font name} -@!font_scaled_size:array [0..max_fonts] of real; {scale factors over $2^{20}$} -@!font_design_size:array [0..max_fonts] of real; {design sizes over $2^{20}$} -@!font_check_sum:array [0..max_fonts] of integer; - {check sum from the |font_def|} -@!font_bc:array [0..max_fonts] of integer; {beginning characters in fonts} -@!font_ec:array [0..max_fonts] of integer; {ending characters in fonts} -@!info_base:array [0..max_fonts] of integer; - {index into |width| and |cmd_ptr| tables} -@!width:array [0..max_widths] of integer; - {character widths, in units $2^{-20}$ of design size} -@!fbase:array [0..max_fonts] of integer; {index into |font_num| for local fonts} -@!ftop:array [0..max_fonts] of integer; - {|font_num| index where local fonts stop} -@!cmd_ptr:array [0..max_widths] of integer; {starting positions in |cmd_buf|} -@!nf:0..max_fonts; {the number of known fonts} -@!vf_ptr:max_fonts..max_fnums; - {next |font_num| entry for virtual font font tables} -@!info_ptr:0..max_widths; {allocation pointer for |width| and |cmd_ptr| tables} -@!n_cmds:0..virtual_space; {number of occupied cells in |cmd_buf|} -@!cur_fbase,@!cur_ftop:0..max_fnums; - {currently applicable part of the |font_num| table} - -@ @<Set init...@>= -nf:=0; info_ptr:=0; font_name[0]:=0; -vf_ptr:=max_fnums; -cur_fbase:=0; cur_ftop:=0; - -@ Printing the name of a given font is easy except that a procedure |print_char| -is needed to actually send an |ASCII_code| to the \.{MPX} file. - -@p @<Declare subroutines for printing strings@>@; -procedure print_font(@!f:integer); {|f| is an internal font number} -var k:0..name_size; {index into |names|} -begin if (f<0)or(f>=nf) then bad_dvi('Undefined font') -else begin for k:=font_name[f] to font_name[f+1]-1 do - print_char(names[k]); - end; -end; - -@ Sometimes a font name is needed as part of an error message. - -@d font_warn(#)==begin err_print('DVItoMP warning: ',#); font_warn_end -@d font_warn_end(#)==err_print_font(#); - history:=warning_given; - end -@d font_abort(#)==begin err_print('DVItoMP abort: ',#); font_abort_end -@d font_abort_end(#)==err_print_font(#); - history:=fatal_error; jump_out; - end - -@p procedure err_print_font(@!f:integer); {|f| is an internal font number} -var k:0..name_size; {index into |names|} -begin for k:=font_name[f] to font_name[f+1]-1 do - err_print(xchr[names[k]]); - err_print_ln(' '); -end; - -@ When we encounter a font definition, we save the name, checksum, and size -information, but we don't actually read the \.{TFM} or \.{VF} file until we -are about to use the font. If a matching font is not already defined, we then -allocate a new internal font number. - -The following subroutine does the necessary things when a \\{fnt\_def} command -is encountered in the \.{DVI} file or in a \.{VF} file. It assumes that the -first argument has already been parsed and is given by the parameter~|e|. - -@p @<Declare a function called |match_font|@>@; -procedure define_font(@!e:integer); {|e| is an external font number} -var @!i:integer; {index into |font_num| and |internal_num|} -@!n:integer; {length of the font name and area} -@!k:integer; {general purpose loop counter} -@!x:integer; {a temporary value for scaled size computation} -begin if nf=max_fonts then abort('DVItoMP capacity exceeded (max fonts=', - max_fonts:1,')!'); -@.DVItoMP capacity exceeded...@> -@<Allocate an index |i| into the |font_num| and |internal_num| tables@>; -@<Read the font parameters into position for font |nf|@>; -internal_num[i]:=match_font(nf,true); -if internal_num[i]=nf then - begin info_base[nf]:=max_widths; {indicate that the info isn't loaded yet} - local_only[nf]:=vf_reading; incr(nf); - end; -end; - -@ @<Allocate an index |i| into the |font_num| and |internal_num| tables@>= -if vf_ptr=nf then abort('DVItoMP capacity exceeded (max font numbers=', - max_fnums:1,')'); -@.DVItoMP capacity exceeded...@> -if vf_reading then - begin font_num[nf]:=0; i:=vf_ptr; decr(vf_ptr); - end -else i:=nf; -font_num[i]:=e - -@ @<Read the font parameters into position for font |nf|@>= -font_check_sum[nf]:=signed_quad; -@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>; -n:=get_byte; area_length[nf]:=n; -n:=n+get_byte; -if font_name[nf]+n>name_size then - abort('DVItoMP capacity exceeded (name size=',name_size:1,')!'); -@.DVItoMP capacity exceeded...@> -font_name[nf+1]:=font_name[nf]+n; -for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=get_byte - -@ The scaled size and design size are stored in \.{DVI} units divided by $2^{20}$. -The units for scaled size are a little different if we are reading a virtual -font, but this will be corrected when the scaled size is used. The scaled size -also needs to be truncated to at most 23 significant bits in order to make -the character width calculation match what \TeX\ does. - -@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>= -x:=signed_quad; -k:=1; -while x>@'40000000 do - begin x:=x div 2; k:=k+k; - end; -font_scaled_size[nf]:=x*k/1048576.0; -if vf_reading then - font_design_size[nf]:=signed_quad*dvi_per_fix/1048576.0 -else font_design_size[nf]:=signed_quad/1048576.0; - -@ @<Glob...@>= -@!dvi_per_fix:real; {converts points scaled $2^{20}$ to \.{DVI} units} - -@ The |match_font| function tries to find a match for the font with internal -number~|ff|, returning |nf| or the number of the matching font. If -|exact=true|, the name and scaled size should match. Otherwise the scaled -size need not match but the font found must be already loaded, not just -defined. - -@<Declare a function called |match_font|@>= -function match_font(ff:integer; exact:boolean):integer; -label done, 99; -var @!f:0..max_fonts; {font number being tested} -@!ss,@!ll:0..name_size; {starting point and length of name of font |ff|} -@!k,@!s:0..name_size; {registers for comparing font names} -begin ss:=font_name[ff]; ll:=font_name[ff+1]-ss; -f:=0; -while f<nf do - begin if f<>ff then - begin @<Compare the names of fonts |f| and |ff|; |goto 99| if they differ@>; - if exact then - begin if abs(font_scaled_size[f]-font_scaled_size[ff]) - <= font_tolerance then - begin if not vf_reading then begin - if local_only[f] then - begin font_num[f]:=font_num[ff]; local_only[f]:=false; - end - else if font_num[f]<>font_num[ff] then goto 99; - end; - goto done; - end; - end - else if info_base[f]<>max_widths then goto done; - end; - 99:incr(f); - end; -done:if f<nf then begin - @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>; - end; -match_font:=f; -end; - -@ @<Compare the names of fonts |f| and |ff|; |goto 99| if they differ@>= -if (area_length[f]<area_length[ff]) or (ll<>font_name[f+1]-font_name[f]) then - goto 99; -s:=font_name[f]; -k:=ll; -while k>0 do - begin decr(k); - if names[s+k]<>names[ss+k] then goto 99; - end - -@ @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>= -if abs(font_design_size[f]-font_design_size[ff]) > font_tolerance then - font_warn('Inconsistent design sizes given for ')(ff) -@.Inconsistent design sizes@> -else if font_check_sum[f]<>font_check_sum[ff] then - font_warn('Checksum mismatch for ')(ff) -@.Checksum mismatch@> - -@* Reading ordinary fonts. -An auxiliary array |in_width| is used to hold the widths as they are -input. The global variable |tfm_check_sum| is set to the check sum that -appears in the current \.{TFM} file. - -@<Glob...@>= -@!in_width:array[0..255] of integer; {\.{TFM} width data in \.{DVI} units} -@!tfm_check_sum:integer; {check sum found in |tfm_file|} - -@ Here is a procedure that absorbs the necessary information from a -\.{TFM} file, assuming that the file has just been successfully reset -so that we are ready to read its first byte. (A complete description of -\.{TFM} file format appears in the documentation of \.{TFtoPL} and will -not be repeated here.) The procedure does not check the \.{TFM} file -for validity, nor does it give explicit information about what is -wrong with a \.{TFM} file that proves to be invalid. The procedure simply -aborts the program if it detects anything amiss in the \.{TFM} data. - -@p procedure in_TFM(@!f:integer); - {input \.{TFM} data for font |f| or abort} -label 9997, {go here when the format is bad} - 9999; {go here to exit} -var k:integer; {index for loops} -@!lh:integer; {length of the header data, in four-byte words} -@!nw:integer; {number of words in the width table} -@!wp:0..max_widths; {new value of |info_ptr| after successful input} -begin @<Read past the header data; |goto 9997| if there is a problem@>; -@<Store character-width indices at the end of the |width| table@>; -@<Read the width values into the |in_width| table@>; -@<Move the widths from |in_width| to |width|@>; -fbase[f]:=0; ftop[f]:=0; -info_ptr:=wp; goto 9999; -9997: font_abort('Bad TFM file for ')(f); -@.Bad TFM file@> -9999: end; - -@ @<Read past the header...@>= -read_tfm_word; lh:=b2*256+b3; -read_tfm_word; font_bc[f]:=b0*256+b1; font_ec[f]:=b2*256+b3; -if font_ec[f]<font_bc[f] then font_bc[f]:=font_ec[f]+1; -if info_ptr+font_ec[f]-font_bc[f]+1>max_widths then - abort('DVItoMP capacity exceeded (width table size=',max_widths:1,')!'); -@.DVItoMP capacity exceeded...@> -wp:=info_ptr+font_ec[f]-font_bc[f]+1; -read_tfm_word; nw:=b0*256+b1; -if (nw=0)or(nw>256) then goto 9997; -for k:=1 to 3+lh do - begin if eof(tfm_file) then goto 9997; - read_tfm_word; - if k=4 then begin - if b0<128 then tfm_check_sum:=((b0*256+b1)*256+b2)*256+b3 - else tfm_check_sum:=(((b0-256)*256+b1)*256+b2)*256+b3; - end; - end; - -@ @<Store character-width indices...@>= -if wp>0 then for k:=info_ptr to wp-1 do - begin read_tfm_word; - if b0>nw then goto 9997; - width[k]:=b0; - end; - -@ No fancy width calculation is needed here because \.{DVItoMP} stores -widths in their raw form as multiples of the design size scaled by $2^{20}$. -The |font_scaled_size| entries have been computed so that the final width -compution can be done in floating point if enough precision is available. - -@<Read the width values into the |in_width| table@>= -for k:=0 to nw-1 do - begin read_tfm_word; - if b0>127 then b0:=b0-256; - in_width[k]:=((b0*@'400+b1)*@'400+b2)*@'400+b3; - end - -@ The width compution uses a scale factor |dvi_scale| that will be introduced -later. It is equal to one when not typesetting a character from a virtual -font. In that case, the following expressions do the width computation that is -so important in \.{DVItype}. It is less important here because it is impractical -to guarantee precise character positioning in \MP\ output. Nevertheless, the -width compution will be precise if reals have at least 46-bit mantissas and -|round(x-.5)| is equivalent to $\lfloor x\rfloor$. It may be a good idea to -modify this computation if these conditions are not met. -@^system dependencies@> - -@<Width of character |c| in font |f|@>= -round(dvi_scale*font_scaled_size[f]*char_width(f)(c)-0.5) - -@ @<Width of character |p| in font |cur_font|@>= -round(dvi_scale*font_scaled_size[cur_font]*char_width(cur_font)(p)-0.5) - -@ @<Move the widths from |in_width| to |width|@>= -if in_width[0]<>0 then goto 9997; {the first width should be zero} -info_base[f]:=info_ptr-font_bc[f]; -if wp>0 then for k:=info_ptr to wp-1 do - width[k]:=in_width[width[k]] - - -@* Reading virtual fonts. -The |in_VF| procedure absorbs the necessary information from a \.{VF} file that -has just been reset so that we are ready to read the first byte. (A complete -description of \.{VF} file format appears in the documention of \.{VFtoVP}). -Like |in_TFM|, this procedure simply aborts the program if it detects anything -wrong with the \.{VF} file. - -@p @<Declare a function called |first_par|@>@; -procedure in_VF(f:integer); - {read \.{VF} data for font |f| or abort} -label 9997, {go here to abort} - 9999; {go here to exit} -var @!p:integer; {a byte from the \.{VF} file} -@!was_vf_reading:boolean; {old value of |vf_reading|} -@!c:integer; {the current character code} -@!limit:integer; {space limitations force character codes to be less than this} -@!w:integer; {a \.{TFM} width being read} -begin was_vf_reading:=vf_reading; vf_reading:=true; -@<Start reading the preamble from a \.{VF} file@>;@/ -@<Initialize the data structures for the virtual font@>;@/ -p:=get_byte; -while p>=fnt_def1 do - begin if p>fnt_def1+3 then goto 9997; - define_font(first_par(p)); - p:=get_byte; - end; -while p<=242 do - begin if eof(vf_file) then goto 9997; - @<Read the packet length, character code, and \.{TFM} width@>; - @<Store the character packet in |cmd_buf|@>; - p:=get_byte; - end; -if p=post then - begin @<Finish setting up the data structures for the new virtual font@>; - goto 9999; - end; -9997:font_abort('Bad VF file for ')(f); -9999: vf_reading:=was_vf_reading; -end; - -@ @<Start reading the preamble from a \.{VF} file@>= -p:=get_byte; -if p<>pre then goto 9997; -p:=get_byte; {fetch the identification byte} -if p<>202 then goto 9997; -p:=get_byte; {fetch the length of the introductory comment} -while p>0 do - begin decr(p); down_the_drain:=get_byte; - end; -tfm_check_sum:=signed_quad; -down_the_drain:=signed_quad; {skip over the design size} - -@ @<Initialize the data structures for the virtual font@>= -ftop[f]:=vf_ptr; -if vf_ptr=nf then abort('DVItoMP capacity exceeded (max font numbers=', - max_fnums:1,')'); -@.DVItoMP capacity exceeded...@> -decr(vf_ptr); -info_base[f]:=info_ptr; -limit:=max_widths-info_base[f];@/ -font_bc[f]:=limit; font_ec[f]:=0 - -@ @<Read the packet length, character code, and \.{TFM} width@>= -if p=242 then - begin p:=signed_quad; c:=signed_quad; w:=signed_quad; - if c<0 then goto 9997; - end -else begin c:=get_byte; w:=get_three_bytes; - end; -if c>=limit then - abort('DVItoMP capacity exceeded (max widths=', max_widths:1,')!'); -@.DVItoMP capacity exceeded...@> -if c<font_bc[f] then font_bc[f]:=c; -if c>font_ec[f] then font_ec[f]:=c; -char_width(f)(c):=w - -@ @<Store the character packet in |cmd_buf|@>= -if n_cmds+p>=virtual_space then - abort('DVItoMP capacity exceeded (virtual font space=',virtual_space:1,')!'); -@.DVItoMP capacity exceeded...@> -start_cmd(f)(c):=n_cmds; -while p>0 do - begin cmd_buf[n_cmds]:=qi(get_byte); - incr(n_cmds); decr(p); - end; -cmd_buf[n_cmds]:=qi(eop); {add the end-of-packet marker} -incr(n_cmds) - -@ There are unused |width| and |cmd_ptr| entries if |font_bc[f]>0| but it isn't -worthwhile to slide everything down just to save a little space. - -@<Finish setting up the data structures for the new virtual font@>= -fbase[f]:=vf_ptr+1; -info_ptr:=info_base[f]+font_ec[f]+1 - - -@* Loading fonts. -The character width information for a font is loaded when the font is selected -for the first time. This information might already be loaded if the font has -already been used at a different scale factor. Otherwise, we look for a \.{VF} -file, or failing that, a \.{TFM} file. All this is done by the |select_font| -function that takes an external font number~|e| and returns the corresponding -internal font number with the width information loaded. - -@p function select_font(@!e:integer):integer; -var @!f:0..max_fonts; {the internal font number} -@!ff:0..max_fonts; {internal font number for an existing version} -@!k:integer; {general purpose loop counter} -begin @<Set |f| to the internal font number that corresponds to |e|, - or |abort| if there is none@>; -if info_base[f]=max_widths then - begin ff:=match_font(f,false); - if ff<nf then @<Make font |f| refer to the width information from font |ff|@> - else begin @<Move the \.{VF} file name into the |cur_name| string@>; - if open_vf_file then in_VF(f) - else begin @<Change \.{.VF} to \.{.TFM} in the |cur_name| string@>; - if not open_tfm_file then font_abort('No TFM file found for ')(f); - @.no TFM file found@> - in_TFM(f); - end; - @<Make sure the checksum in the font file matches the one given in the - |font_def| for font |f|@>; - end; - @<Do any other initialization required for the new font |f|@>; - end; -select_font:=f; -end; - -@ @<Set |f| to the internal font number that corresponds to |e|,...@>= -if cur_ftop<=nf then cur_ftop:=nf; -font_num[cur_ftop]:=e; -k:=cur_fbase; -while (font_num[k]<>e)or local_only[k] do incr(k); -if k=cur_ftop then abort('Undefined font selected'); -f:=internal_num[k] - -@ @<Make font |f| refer to the width information from font |ff|@>= -begin font_bc[f]:=font_bc[ff]; -font_ec[f]:=font_ec[ff]; -info_base[f]:=info_base[ff]; -fbase[f]:=fbase[ff]; -ftop[f]:=ftop[ff]; -end - -@ If |area_length[f]=0|, i.e., if no font directory has been specified, -\.{DVItoMP} 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} - -@<Glob...@>= -@!default_directory:packed array[1..default_directory_name_length] of char; - -@ @<Set init...@>= -default_directory:=default_directory_name; - -@ The string |cur_name| is supposed to be set to the external name of the -\.{VF} file for the current font. This usually means that we need to -prepend the name of the default directory, and -to append the suffix `\.{.VF}'. Furthermore, we change lower case letters -to upper case, since |cur_name| is a \PASCAL\ string. -@^system dependencies@> - -@<Move the \.{VF} file name into the |cur_name| string@>= -for k:=1 to name_length do cur_name[k]:=' '; -if area_length[f]=0 then - begin for k:=1 to default_directory_name_length do - cur_name[k]:=default_directory[k]; - l:=default_directory_name_length; - end -else l:=0; -for k:=font_name[f] to font_name[f+1]-1 do - begin incr(l); - if l+3>name_length then - abort('DVItoMP capacity exceeded (max font name length=', - name_length:1,')!'); -@.DVItoMP capacity exceeded...@> - if (names[k]>="a")and(names[k]<="z") then - cur_name[l]:=xchr[names[k]-@'40] - else cur_name[l]:=xchr[names[k]]; - end; -cur_name[l+1]:='.'; cur_name[l+2]:='V'; cur_name[l+3]:='F' - -@ It is fairly simple to change a \.{VF} file name to a \.{TFM} file name. -@^system dependencies@> - -@<Change \.{.VF} to \.{.TFM} in the |cur_name| string@>= -l:=area_length[f]; -if l=0 then l:=default_directory_name_length; -l:=l+font_name[f+1]-font_name[f]; -if l+4>name_length then - abort('DVItoMP capacity exceeded (max font name length=', - name_length:1,')!'); -@.DVItoMP capacity exceeded...@> -cur_name[l+2]:='T'; cur_name[l+3]:='F'; cur_name[l+4]:='M' - -@ @<Make sure the checksum in the font file matches the one given in the...@>= -begin if (font_check_sum[f]<>0)and(tfm_check_sum<>0)and@| - (font_check_sum[f]<>tfm_check_sum) then - begin err_print('DVItoMP warning: Checksum mismatch for '); -@.Checksum mismatch@> - err_print_font(f); - if history=spotless then history:=cksum_trouble; - end; -end - -@* Low level output routines. -One of the basic output operations is to write a \MP\ string expression for -a sequence of characters to be typeset. The main difficulties are that such -strings can contain arbitrary eight-bit bytes and there is no fixed limit on -the length of the string that needs to be produced. In extreme cases this -can lead to expressions such as -$$\vcenter{ - \hbox{\.{char7\&char15\&char31\&"?FWayzz"}} - \hbox{\.{\&"zzaF"\&char15\&char3\&char31}} - \hbox{\.{\&"Nxzzzzzzzwvtsqo"}}} -$$ - -@ A global variable |state| keeps track of the output process. -When |state=normal| we have begun a quoted string and the next character -should be a printable character or a closing quote. When |state=special| -the last thing printed was a ``\.{char}'' construction or a closing quote -and an ampersand should come next. The starting condition |state=initial| -is a lot like |state=special|, except no ampersand is required. - -@d special=0 {the |state| after printing a ``\.{char}'' expression} -@d normal=1 {the |state| value in a quoted string} -@d initial=2 {initial |state|} - -@<Glob...@>= -state:special..initial; {controls the process of printing a string} -print_col:0..line_length; - {there are at most this many characters on the current line} - -@ To print a string on the \.{MPX} file, initialize |print_col|, ensure that -|state=initial|, and pass the characters one-at-a-time to |print_char|. - -@<Declare subroutines for printing strings@>= -procedure print_char(@!c:eight_bits); -var @!printable:boolean; {is it safe to print |xchr[c]|?} -@!l:integer; {number of characters to print |c| or the \.{char} expression} -begin printable:=(c>=" ")and(c<="~")and(c<>""""); -if printable then l:=1 -else if c<10 then l:=5 -else if c<100 then l:=6 -else l:=7; -if print_col+l>line_length-2 then - begin if state=normal then - begin print('"'); state:=special; - end; - print_ln(' '); - print_col:=0; - end; -@<Print |c| and update |state| and |print_col|@>; -end; - -@ @<Print |c| and update |state| and |print_col|@>= -if state=normal then - if printable then print(xchr[c]) - else begin print('"&char',c:1); - print_col:=print_col+2; - end -else begin if state=special then - begin print('&'); incr(print_col); - end; - if printable then - begin print('"',xchr[c]); incr(print_col); - end - else print('char',c:1); - end; -print_col:=print_col+l; -if printable then state:=normal @+else state:=special - -@ The |end_char_string| procedure gets the string ended properly and ensures -that there is room for |l| more characters on the output line. - -@<Declare subroutines for printing strings@>= -procedure end_char_string(@!l:integer); -begin while state>special do - begin print('"'); - incr(print_col); - decr(state); - end; -if print_col+l>line_length then - begin print_ln(' '); print_col:=0; - end; -state:=initial; {get ready to print the next string} -end; - -@ Since |end_char_string| resets |state:=initial|, all we have to do is set -|state:=initial| once at the beginning. - -@<Set init...@>= -state:=initial; - -@ Characters and rules are positioned according to global variables |h| and~|v| -as will be explained later. We also need scale factors that convert quantities -to the right units when they are printed in the \.{MPX} file. - -Even though all variable names in the \MP\ output are made local via \.{save} -commands, it is still desirable to preceed them with underscores. This makes -the output more likely to work when used in a macro definition, since the -generated variables names must not collide with formal parameters in such -cases. - -@<Glob...@>= -@!h,@!v:integer; {the current position in \.{DVI} units} -@!conv:real; {converts \.{DVI} units to \MP\ points} -@!mag:real; {magnification factor times 1000} - -@ @p @<Declare a procedure called |finish_last_char|@>@; -procedure do_set_char(@!f,@!c:integer); -begin if (c<font_bc[f])or(c>font_ec[f]) then - abort('attempt to typeset invalid character ',c:1); -@.attempt to typeset...@> -if (h<>str_h2)or(v<>str_v)or(f<>str_f)or(dvi_scale<>str_scale) then - begin if str_f>=0 then finish_last_char - else if not fonts_used then - @<Prepare to output the first character on a page@>; - if not font_used[f] then - @<Prepare to use font |f| for the first time on a page@>; - print('_s('); print_col:=3;@/ - str_scale:=dvi_scale; str_f:=f; str_v:=v; str_h1:=h; - end; -print_char(c); -str_h2:=h+@<Width of character |c| in font |f|@>; -end; - -@ @<Glob...@>= -@!font_used:array[0..max_fonts] of boolean; - {has this font been used on this page?} -@!fonts_used:boolean; {has any font been used on this page?} -@!rules_used:boolean; {has any rules been set on this page?} -@!str_h1,str_v:integer; {starting position for current output string} -@!str_h2:integer; {where the current output string ends} -@!str_f:integer; {internal font number for the current output string} -@!str_scale:real; {value of |dvi_scale| for the current output string} - -@ The |font_used| array is not initialized until it is actually time to output -a character. - -@<Prepare to output the first character on a page@>= -begin k:=0; -while (k<nf) do - begin font_used[k]:=false; incr(k); - end; -fonts_used:=true; -print_ln('string _n[];'); -print_ln('vardef _s(expr _t,_f,_m,_x,_y)='); -print_ln(' addto _p also _t infont _f scaled _m shifted (_x,_y); enddef;'); -end - -@ @<Do any other initialization required for the new font |f|@>= -font_used[f]:=false; - -@ @<Prepare to use font |f| for the first time on a page@>= -begin font_used[f]:=true; -print('_n',f:1,'='); -print_col:=6; -print_font(f); -end_char_string(1); -print_ln(';'); -end - -@ We maintain the invariant that |str_f=-1| when there is no output string -under construction. - -@<Declare a procedure called |finish_last_char|@>= -procedure finish_last_char; -var @!m,@!x,@!y:real; - {font scale factor and \MP\ coordinates of reference point} -begin if str_f>=0 then - begin m:=str_scale*font_scaled_size[str_f]*mag/font_design_size[str_f];@/ - x:=conv*str_h1; y:=conv*(-str_v); - if (abs(x)>=4096.0)or(abs(y)>=4096.0)or(m>=4096.0)or(m<0) then - begin warn('text scaled ',m:1:1,@| - ' at (',x:1:1,',',y:1:1,') is out of range'); - end_char_string(60); - end - else end_char_string(40); - print_ln(',_n',str_f:1,',',m:1:5,',',x:1:4,',',y:1:4,');'); - str_f:=-1; - end; -end; - -@ Setting rules is fairly simple. - -@p procedure do_set_rule(@!ht,@!wd:integer); -var @!xx1,@!yy1,@!xx2,@!yy2,@!ww:real; - {\MP\ coordinates of lower-left and upper-right corners} -begin if wd=1 then @<Handle a special rule that determines the box size@> -else if (ht>0)or(wd>0) then - begin if str_f>=0 then finish_last_char; - if not rules_used then - begin rules_used:=true;@/ - print_ln('interim linecap:=0;');@/ - print_ln('vardef _r(expr _a,_w) ='); - print_ln(' addto _p doublepath _a withpen pencircle scaled _w enddef;'); - end; - @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke - and |ww| the desired stroke width@>; - if (abs(xx1)>=4096.0)or(abs(yy1)>=4096.0)or@| - (abs(xx2)>=4096.0)or(abs(yy2)>=4096.0)or(ww>=4096.0) then - warn('hrule or vrule near (',xx1:1:1,',',yy1:1:1,') is out of range'); - print_ln('_r((',xx1:1:4,',',yy1:1:4,')..(',xx2:1:4,',',yy2:1:4, - '), ',ww:1:4,');'); - end; -end; - -@ @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke...@>= -xx1:=conv*h; -yy1:=conv*(-v); -if wd>ht then - begin xx2:=xx1+conv*wd; - ww:=conv*ht;@/ - yy1:=yy1+0.5*ww; - yy2:=yy1; - end -else begin yy2:=yy1+conv*ht; - ww:=conv*wd;@/ - xx1:=xx1+0.5*ww; - xx2:=xx1; - end - -@ Rules of width one dvi unit are not typeset since \.{MPtoTeX} adds an -extraneous rule of this width in order to allow \.{DVItoMP} to deduce the -dimensions of the boxes it ships out. The box width is the left edge of the -last such rule; the height and depth are at the top and bottom of the rule. -There should be only one special rule per picture but there could be more if -the user tries to typeset his own one-dvi-unit rules. In this case the -dimension-determining rule is the last one in the picture. - -@<Handle a special rule that determines the box size@>= -begin pic_wd:=h; pic_dp:=v; pic_ht:=ht-v; -end - -@ @<Glob...@>= -pic_dp, pic_ht, pic_wd: integer; {picture dimensions from special rule} - -@ The following initialization and clean-up is required. We do a little more -initialization than is absolutely necessary since some compilers might complain -if the variables are uninitialized when |do_set_char| tests them. - -@p procedure start_picture; -begin fonts_used:=false; -rules_used:=false; -str_f:=-1;@/ -str_v:=0; str_h2:=0; str_scale:=1.0; {values don't matter} -print_ln('begingroup save _p,_r,_s,_n; picture _p; _p=nullpicture;'); -end; -@# -procedure stop_picture; -var @!w,@!h,@!dd:real; {width, height, negative depth in PostScript points} -begin if str_f>=0 then finish_last_char; -@<Print a \&{setbounds} command based on picture dimensions@>; -print_ln('_p endgroup'); -end; - -@ @<Print a \&{setbounds} command based on picture dimensions@>= -dd:=-pic_dp*conv; -w:=conv*pic_wd; h:=conv*pic_ht;@/ -print_ln('setbounds _p to (0,',dd:1:4,')--(',w:1:4,',',dd:1:4,')--'); -print_ln(' (',w:1:4,',',h:1:4,')--(0,',h:1:4,')--cycle;') - -@* Translation to symbolic form. -The main work of \.{DVItoMP} is accomplished by the |do_dvi_commands| -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. It is also capable of executing the typesetting commands for -a character in a virtual font. - -@ The definition of \.{DVI} files refers to six registers, -$(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units. -These units come directly from the input file except they need to be -rescaled when typesetting characters from a virtual font. -The stack of $(h,v,w,x,y,z)$ values is represented by six arrays -called |hstack|, \dots, |zstack|. - -@<Glob...@>= -@!w,@!x,@!y,@!z:integer; - {current state values (|h| and |v| have already been declared)} -@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack: - array [0..stack_size] of integer; {pushed down values in \.{DVI} units} -@!stk_siz:integer; {the current stack size} -@!dvi_scale:real; {converts units of current input source to \.{DVI} units} - -@ @<Do initialization required before starting a new page@>= -dvi_scale:=1.0; -stk_siz:=0; -h:=0; v:=0 - -@ Next, we need procedures to handle |push| and |pop| commands. - -@p procedure do_push; -begin if stk_siz=stack_size then - abort('DVItoMP capacity exceeded (stack size=',stack_size:1,')'); -@.DVItoMP capacity exceeded...@> -hstack[stk_siz]:=h; vstack[stk_siz]:=v; wstack[stk_siz]:=w; -xstack[stk_siz]:=x; ystack[stk_siz]:=y; zstack[stk_siz]:=z; -incr(stk_siz); -end; -@# -procedure do_pop; -begin if stk_siz=0 then bad_dvi('attempt to pop empty stack') -else begin decr(stk_siz); - h:=hstack[stk_siz]; v:=vstack[stk_siz]; w:=wstack[stk_siz]; - x:=xstack[stk_siz]; y:=ystack[stk_siz]; z:=zstack[stk_siz]; - end; -end; - -@ We need to define the |set_virtual_char| procedure now because it is -mutually recursive with |do_dvi_commands|. This is really a supervisory -@^recursion@> -procedure that calls |do_set_char| or adjusts the input source to read -typesetting commands for a character in a virtual font. - -@p procedure do_dvi_commands;forward;@t\2@> -procedure set_virtual_char(@!f,@!c:integer); -var @!old_scale:real; {original value of |dvi_scale|} -@!old_buf_ptr:0..virtual_space; {original value of the input pointer |buf_ptr|} -@!old_fbase,@!old_ftop:0..max_fnums; - {originally applicable part of the |font_num| table} -begin if fbase[f]=0 then do_set_char(f,c) -else begin old_fbase:=cur_fbase; old_ftop:=cur_ftop; - cur_fbase:=fbase[f]; cur_ftop:=ftop[f];@/ - old_scale:=dvi_scale; - dvi_scale:=dvi_scale*font_scaled_size[f]; - old_buf_ptr:=buf_ptr; - buf_ptr:=start_cmd(f)(c);@/ - do_push; - do_dvi_commands; - do_pop;@/ - buf_ptr:=old_buf_ptr; - dvi_scale:=old_scale; - cur_fbase:=old_fbase; cur_ftop:=old_ftop; - end; -end; - -@ Before we get into the details of |do_dvi_commands|, 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) - -@<Declare a function called |first_par|@>= -function first_par(o:eight_bits):integer; -begin first_par:=0; {for compiler warnings} -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 the |do_dvi_commands| procedure. - -@p procedure do_dvi_commands; -label 9999; -var o:eight_bits; {operation code of the current command} -@!p,@!q:integer; {parameters of the current command} -@!cur_font:integer; {current internal font number} -begin if (cur_fbase<cur_ftop) and (buf_ptr<virtual_space) then - cur_font:=select_font(font_num[cur_ftop-1]) {select first local font} -else cur_font:=max_fnums+1; {current font is undefined} -w:=0; x:=0; y:=0; z:=0; {initialize the state variables} -while true do @<Translate the next command in the \.{DVI} file; - |goto 9999| if it was |eop|@>; -9999: do_nothing; -end; - -@ The multiway switch in |first_par|, above, was organized by the length -of each command; the one in |do_dvi_commands| is organized by the semantics. - -@ @<Translate the next command...@>= -begin o:=get_byte; p:=first_par(o); -if eof(dvi_file) then bad_dvi('the DVI file ended prematurely'); -@.the DVI file ended prematurely@> -if o<set1+4 then {|set_char_0| through |set_char_127|, |set1| through |set4|} - begin if cur_font>max_fnums then begin - if vf_reading then - abort('no font selected for character ',p:1,' in virtual font') - else bad_dvi('no font selected for character ',p:1); - end; -@.no font selected@> - set_virtual_char(cur_font,p); - h:=h+@<Width of character |p| in font |cur_font|@>; - end -else case o of - four_cases(put1): set_virtual_char(cur_font,p); - set_rule: begin q:=trunc(signed_quad*dvi_scale); - do_set_rule(trunc(p*dvi_scale),q); - h:=h+q; - end; - put_rule: do_set_rule(trunc(p*dvi_scale),trunc(signed_quad*dvi_scale)); - @t\4@>@<Additional cases for translating \.{DVI} command |o| with - first paramter |p|@>@; - undefined_commands:bad_dvi('undefined command ',o:1); -@.undefined command@> - end; {all cases have been enumerated} -end - -@ @<Additional cases for translating \.{DVI} command |o|...@>= -four_cases(xxx1): for k:=1 to p do - down_the_drain:=get_byte; -pre,post,post_post: bad_dvi('preamble or postamble within a page!'); -@.preamble or postamble within a page@> - -@ @<Additional cases for translating \.{DVI} command |o|...@>= -nop: do_nothing; -bop: bad_dvi('bop occurred before eop'); -@.bop occurred before eop@> -eop: goto 9999; -push: do_push; -pop: do_pop; - -@ @<Additional cases for translating \.{DVI} command |o|...@>= -four_cases(right1):h:=h+trunc(p*dvi_scale); -w0,four_cases(w1):begin w:=trunc(p*dvi_scale); h:=h+w; - end; -x0,four_cases(x1):begin x:=trunc(p*dvi_scale); h:=h+x; - end; -four_cases(down1):v:=v+trunc(p*dvi_scale); -y0,four_cases(y1):begin y:=trunc(p*dvi_scale); v:=v+y; - end; -z0,four_cases(z1):begin z:=trunc(p*dvi_scale); v:=v+z; - end; - -@ @<Additional cases for translating \.{DVI} command |o|...@>= -sixty_four_cases(fnt_num_0),four_cases(fnt1): - cur_font:=select_font(p); -four_cases(fnt_def1): define_font(p); - -@* The main program. -Now we are ready to put it all together. This is where \.{DVItoMP} starts, -and where it ends. - -@p begin initialize; {get all variables initialized} -@<Process the preamble@>; -open_mpx_file; -print_ln(banner); -begin while true do - begin @<Advance to the next |bop| command@>; - for k:=0 to 10 do down_the_drain:=signed_quad; - @<Do initialization required before starting a new page@>; - start_picture; - do_dvi_commands; - if stk_siz<>0 then bad_dvi('stack not empty at end of page'); -@.stack not empty...@> - stop_picture; - print_ln('mpxbreak'); - end; -done:end; -final_end:end. - -@ The main program needs a few global variables in order to do its work. - -@<Glob...@>= -@!k,@!p:integer; {general purpose registers} -@!numerator,@!denominator:integer; {stated conversion ratio} - -@ @<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 - warn('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} -while p>0 do - begin decr(p); down_the_drain:=get_byte; - end - -@ The conversion factor |conv| is figured as follows: There are exactly -|n/d| decimicrons per \.{DVI} unit, and 254000 decimicrons per inch, -and |resolution| pixels per inch. Then we have to adjust this -by the stated amount of magnification. No such adjustment is needed for -|dvi_per_fix| since it is used to convert design sizes. - -@<Compute the conversion factor@>= -numerator:=signed_quad; denominator:=signed_quad; -if (numerator<=0)or(denominator<=0) then - bad_dvi('bad scale ratio in preamble'); -@.bad scale ratio@> -mag:=signed_quad/1000.0; -if mag<=0.0 then bad_dvi('magnification isn''t positive'); -@.magnification isn't positive@> -conv:=(numerator/254000.0)*(72.0/denominator)*mag; -dvi_per_fix:=(254000.0/numerator)*(denominator/72.27)/1048576.0; - -@ @<Advance to the next |bop| command@>= -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 goto done; -if k<>bop then bad_dvi('missing bop'); -@.missing bop@> - - -@* System-dependent changes. -This section should be replaced, if necessary, by changes to the program -that are necessary to make \.{DVItoMP} work at a particular installation. -It is usually best to design your change file so that all changes to -previous sections preserve the section numbering; then everybody's version -will be consistent with the printed program. More extensive changes, -which introduce new sections, can be inserted here; then only the index -itself will get a new section number. -@^system dependencies@> - -@* Index. -Pointers to error messages appear here together with the section numbers -where each ident\-i\-fier is used. |