summaryrefslogtreecommitdiff
path: root/dviware/ln03/rose
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /dviware/ln03/rose
Initial commit
Diffstat (limited to 'dviware/ln03/rose')
-rw-r--r--dviware/ln03/rose/clink.opt1
-rw-r--r--dviware/ln03/rose/dvi2ln3.c1654
-rw-r--r--dviware/ln03/rose/dvi2ln3.dif109
-rw-r--r--dviware/ln03/rose/dvi2ln3.lis1602
-rw-r--r--dviware/ln03/rose/dvi2ln3nft.c1145
-rw-r--r--dviware/ln03/rose/ffc.pas277
-rw-r--r--dviware/ln03/rose/ffc.web991
-rw-r--r--dviware/ln03/rose/forpxl.fdl4
-rw-r--r--dviware/ln03/rose/ln03bis.mem632
-rw-r--r--dviware/ln03/rose/ln03topp.pli1255
-rw-r--r--dviware/ln03/rose/lnfinc.pli35
-rw-r--r--dviware/ln03/rose/newffc.c1293
-rw-r--r--dviware/ln03/rose/older_driver.mem84
-rw-r--r--dviware/ln03/rose/picmode.pli529
-rw-r--r--dviware/ln03/rose/specials.c480
-rw-r--r--dviware/ln03/rose/toppnft.pli654
16 files changed, 10745 insertions, 0 deletions
diff --git a/dviware/ln03/rose/clink.opt b/dviware/ln03/rose/clink.opt
new file mode 100644
index 0000000000..a01884977f
--- /dev/null
+++ b/dviware/ln03/rose/clink.opt
@@ -0,0 +1 @@
+sys$share:vaxcrtl/share
diff --git a/dviware/ln03/rose/dvi2ln3.c b/dviware/ln03/rose/dvi2ln3.c
new file mode 100644
index 0000000000..56765ef36d
--- /dev/null
+++ b/dviware/ln03/rose/dvi2ln3.c
@@ -0,0 +1,1654 @@
+/* Dvi2ln3 translates a TeX DVI file to an LN03 format file.
+
+Dvi2ln3 is still being developed. Copyright (c) 1985, 1986 by Digital
+Equipment Corporation, Maynard, Massachusetts, USA. Author: Flavio Rose,
+...!decwrl!dec-rhea!dec-dvinci!rose.
+
+Dvi2ln3 is based on the publicly-available program DVItype, written by
+David R. Fuchs of Stanford University; and also on earlier DEC programs,
+Dvi2lng, Topp and LN03Topp, for the LN01 and LN03 laser printers. This
+program is not a DEC product and is not guaranteed to work.
+
+Dvi2ln3 is written in VAX C. Specific VAX and VMS dependencies have
+generally been avoided, however. The reader may find them by searching for
+the strings VAX and VMS in this file.
+
+During development, double square brackets [[ ]] in a comment indicate some
+places where the code needs to be improved.
+
+[[Among the useful things that still need to be done: differentiating
+between int and long variables so this can be ported more easily to 16-bit
+architectures; testing to see if malloc returns 0; a \special for ROM
+fonts.]]
+
+Development history:
+
+Feb. 85: Did some early work on Dvi2ln3, translating bits and pieces
+ of Dvi2lng. Concluded, however, that it was better to translate
+ LN03Topp and Dvi2lng into C first. The reason for this is that code
+ which mimics an existing program is easier to write and to test.
+
+5/15/85: At this date, the old LN03Topp program has been successfully
+ translated into C (except for the ability to read files over
+ DECnet). At this point, did some further cleanup of the Dvi2ln3
+ source.
+
+ 6/4/85: Coding of Dvi2ln3 begins in earnest. Merging bits and
+ pieces from various places. Chucking chunks and all that
+ junk from LN03Topp; pass2 is just straightforward translation.
+
+6/22/85: Cleanup after initial debugging. Ready to distribute.
+
+10/8/85: Adding \special's to imitate Textset's DVIAPS program.
+ Corrected bug in parsing of \specials. Version 1.
+
+10/15/85: More changes for specials. Version 2.
+
+11/8/85: Fixed bug in ln03:defpoint \special. Version 3.
+
+11/15/85: Changed way information about font size is passed back
+ by add_txf_to_lnf. Add PXL-reading capability. Version 4.
+
+11/21/85: Error handling has been reformed. Still version 4.
+
+12/12/85: Bug correction in handling of plotfile special. Version 5.
+
+1/7/86: Clear all fonts from memory at beginning of document. Version 6.
+
+1/16/86: Fixed bug in dvi2ln3nft.c, nftf was not being closed after use.
+ Version 7. (Bug found by Mark DeVries.)
+
+3/4/86: Support for landscape added -- very easy, if only I had
+ known. Fixed bug found by Mark DeVries, error returned
+ if \special selects device other than LN03. Version 8.
+
+3/28/86: Conditionalization for Ultrix. #ifndef vms ==
+ #ifdef ultrix. Still emits 8-bit characters. Should be
+ unchanged under VMS. getenv is used to replace
+ logicals. Version 9.
+
+4/4/86: Added support for 7-bit only environments,
+ conditionalized on the SEVENBIT symbol. Still version 9.
+
+4/16/86: Final changes to make compilation under Ultrix
+ clean. Still version 9.
+
+*/
+
+#ifdef vms
+#include stdio
+#include ctype
+#else
+#include <stdio.h>
+#include <ctype.h>
+#endif
+
+/* Ultrix seems to want this: */
+
+#ifndef vms
+char *strchr(), *malloc(), *getenv();
+#define SEVENBIT 1
+#endif
+
+/* In VMS, we declare all global variables to be globaldef.
+This is not really necessary, just an old habit from working
+with other VMS languages. It's done with #define's, so it
+may easily be undone when porting the program to other
+systems. */
+
+#ifdef vms
+#define GLOBAL globaldef
+#define EXTERN globalref
+#else
+#define GLOBAL
+#define EXTERN extern
+#endif
+
+
+/* Here begins a long list of global variables. */
+
+/* hoff is the horizontal offset in pixels to be added to all dimensions
+read in; voff is the corresponding vertical offset. */
+
+GLOBAL int hoff, voff;
+GLOBAL FILE *dvifile,*tfmfile,*outfile;
+
+/* Each page in a DVI file is identified by ten longwords, which appear
+immediately after each bop (beginning of page) command. Dvi2ln3, like
+DVItype, supports certain options that allow one to print only selected
+pages. The following variables are used for this purpose.
+
+Num_pages is a count of how many pages have been passed to the output.
+Max_pages is the user-specified maximum number of pages to pass.
+How_many_counts denotes the number of identifying longwords to take into
+account when searching for the user-specified starting page. The user
+specifies the starting page as the first one whose identifying longwords
+match certain specified values, stored in start_page. However, identifying
+longword i is only required to match the value in start_page[i] if
+use_count[i] is nonzero. */
+
+GLOBAL long int num_pages,max_pages;
+GLOBAL int how_many_counts,use_count[10];
+GLOBAL long int start_page[10];
+
+/* When the output file can have no characters with code >
+127, the user should employ a dvi2ln3 which is recompiled
+with the symbol SEVENBIT defined. Such a dvi2ln3 will run slower
+than one which is allowed eight bits, and produce larger
+output files. */
+
+#ifdef SEVENBIT
+GLOBAL int right7;
+#endif
+
+/* FILESPECLEN is the maximum size of a file specification under VMS.
+[[Perhaps arrays of fixed size should not be used, but rather malloc should
+be employed...]] */
+
+#define FILESPECLEN 252
+
+/* landscape says whether we are printing in landscape or portrait. */
+
+GLOBAL char landscape;
+
+/* The main routine deals with the command line arguments, opens the dvi
+file, and then calls various other routines to handle the "passes". There
+are two passes, each of which involves reading the DVI file from start to
+finish; in between, the font load is constructed. */
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ int status,i,jnam,jext;
+ char infnam[FILESPECLEN];
+
+ if (argc < 2) { printf("\n Usage: Dvi2ln3 <filename> <options>");
+ goto exit_label; }
+
+ printf("\n Dvi2ln3 9");
+
+/* [[The following could bomb if argv[1] is longer than FILESPECLEN
+characters. Sigh.]] */
+
+ strcpy(infnam,argv[1]);
+
+/* The extension of the input file defaults to .dvi. Locate the filename
+field within the filespec parameter, set jname to point to beginning, jext
+to point to one place after end. [[This code is of course dependent on the
+VMS filespec syntax.]] */
+
+ find_filename(infnam,&jnam,&jext);
+ if (infnam[jext] == '\0') strcat(infnam,".dvi");
+ dvifile = fopen(infnam,"r");
+ if (dvifile == NULL) { printf("\n Couldn't open dvi file");
+ goto exit_label; }
+
+/* Now decipher the options off the command line. */
+
+#ifdef SEVENBIT
+ right7 = 0;
+#endif
+ max_pages = 1000000000;
+ how_many_counts = 0;
+ hoff = 300;
+ voff = 300;
+ landscape = 0;
+ for (i=2; i<argc; i++) command_line_options(argv[i]);
+
+/* Now do the passes */
+
+ status = pass1();
+ if (status != 0) goto exit_label;
+#ifdef vms
+ if (rewind(dvifile) == -1) {
+ printf("\n Couldn't rewind dvi file.");
+ goto exit_label;
+ }
+#else
+ rewind(dvifile);
+#endif
+
+ status = open_output_file(infnam,jnam,jext,".ln3");
+ if (status != 0) { printf("\n Couldn't open output file");
+ goto exit_label; }
+
+ status = make_font_load();
+ if (status != 0) goto exit_label;
+
+ pass2();
+
+ fprintf(outfile,"\033c");
+ fclose(dvifile);
+ fclose(outfile);
+
+exit_label:
+#ifndef vms
+ printf("\n");
+#endif
+ ;
+}
+
+/* Find_filename finds the filename part of a filespec
+passed in s, returning the index of the first character in
+*ns, and the index of the character after the last in *ne.
+[[This works under VMS and Unix. MS-DOS still needs work.]]
+*/
+
+int find_filename(s,ns,ne)
+char s[];
+int *ns,*ne;
+{
+ int jnam,jext,j,slen;
+
+ slen = strlen(s);
+ jnam = 0;
+ for (j = slen-1; j >= 0; j--) {
+#ifdef vms
+ if (s[j] == ':' || s[j] == ']' ||
+ s[j] == '>') {
+#else
+ if (s[j] == '/') {
+#endif
+ jnam = j+1;
+ break;
+ }
+ }
+
+ jext = slen;
+ for (j = jnam; j < slen; j++) {
+#ifdef vms
+ if (s[j] == '.' || s[j] == ';') {
+#else
+ if (s[j] == '.') {
+#endif
+ jext = j;
+ break;
+ }
+ }
+
+ *ns = jnam;
+ *ne = jext;
+}
+
+/* ERROR HANDLING: Errors should be reported as close to the
+source as possible, so that the maximum amount of
+information is available to the user to identify the error.
+
+Errors in the format of DVI or TFM files are not reported
+specifically, since there exist programs, DVItype and
+TFtoPL, which diagnose errors in such files. This program
+accepts some incorrect DVI files, for example, those with a
+bad postamble or bad backpointers. [[However, someday we may
+rewrite the program to select pages by using these features
+of the DVI format, rather than by skipping unwanted pages.]]
+*/
+
+GLOBAL char *bad_DVI_message = "\nBad DVI file - check it with DVItype";
+
+/* Command_line_options reads and processes options off the
+string t. At this time, four options are supported.
+
+The N option sets the maximum number of pages to be printed.
+Its syntax is /N=<integer> .
+
+The H option modifies the default horizontal offset.
+
+The V option modifies the default vertical offset.
+
+The S option has syntax /S=<pagespec>{.<pagespec>}* where the {}* denotes
+repetition, and a <pagespec> is either an integer or * to indicate any
+value. An example would be "/S=*.8.3".
+
+The meaning of the S option is as follows: In a DVI file, pages are
+identified by ten longword values which follow each bop (beginning of page)
+command. The value of the S option indicates which page to start printing
+on. For example, "*.8.3" means start printing at the first page whose
+second identifying longword is 8 and third is 3.
+
+The L option says print in landscape.
+
+[[Is a European option needed?]] */
+
+int command_line_options(t)
+char *t;
+{
+ long int i,k;
+ char *u;
+
+ while ((t = strchr(t,'/')) != 0) {
+ t++;
+ if (toupper(t[0]) == 'N' && t[1] == '='
+ && (sscanf(&t[2],"%ld",&k) == 1))
+ max_pages = k;
+ if (toupper(t[0]) == 'H' && t[1] == '='
+ && (sscanf(&t[2],"%ld",&k) == 1))
+ hoff = k;
+ if (toupper(t[0]) == 'V' && t[1] == '='
+ && (sscanf(&t[2],"%ld",&k) == 1))
+ voff = k;
+ if (toupper(t[0]) == 'L') landscape = 1;
+ else if (toupper(t[0]) == 'S' && t[1] == '=') {
+ t += 2;
+ how_many_counts = 0;
+ for (;;) {
+ if (t[0] == '*') {
+ use_count[how_many_counts] = 0;
+ how_many_counts++;
+ }
+ else if (sscanf(t,"%ld",&k) != 0) {
+ use_count[how_many_counts] = 1;
+ start_page[how_many_counts] = k;
+ how_many_counts++;
+ } else break;
+ u = strchr(t,'.');
+ if (u == 0) break;
+ t = &u[1];
+ }
+ }
+ }
+}
+
+/* Open_output_file opens one of the output files, using the file pointer
+outfile. The name of the output file is obtained by appending the string
+ext to the substring of infnam beginning at jnam and ending at jext.
+
+[[This code contains a VMS dependency. We use creat followed by fdopen to
+open the file as a normal VMS file ("rat=cr","rfm=var") rather than a
+STREAM_LF file. Beginning with VAX C V2.0, this can be done by just calling
+fopen.]] */
+
+int open_output_file(infnam,jnam,jext,ext)
+char *infnam,*ext;
+int jnam,jext;
+{
+ char outfnam[FILESPECLEN];
+ int jj;
+
+ strcpy(outfnam,&infnam[jnam]);
+ strcpy(&outfnam[jext-jnam],ext);
+
+#ifdef vms
+ jj = creat(outfnam,0,"rat=cr","rfm=var");
+#else
+ jj = creat(outfnam,0700);
+#endif
+ if (jj == -1) return(1);
+ outfile = fdopen(jj,"w");
+ if (outfile == NULL) return(1);
+
+ return(0);
+}
+
+/* Bytes are read one by one from the DVI file using getc. We use an
+overlay, as suggested in the DVItype documentation, to combine these bytes
+into larger integers. [[Note that this technique relies on the fact that
+numbers on the VAX are stored with the least significant byte first. The
+macros below would have to be changed if the program were to be ported to a
+machine architecture for which this is not so.]]
+
+[[The use of getc is expensive, since getc is a function in VAX C V2.0.
+Eventually, one would want to rewrite these macros to work like the old
+getc macro.]] */
+
+GLOBAL union lc { long int l; unsigned long int ul; char c[4];
+ unsigned char uc[4]; } lcx;
+
+#define two_bytes_u lcx.uc[1] = getc(dvifile); \
+ lcx.uc[0] = getc(dvifile); lcx.uc[2] = 0; lcx.uc[3] = 0
+
+#define two_bytes_s lcx.c[1] = getc(dvifile); \
+ lcx.c[0] = getc(dvifile); \
+ if (lcx.c[1] >= 0) { lcx.uc[2] = 0; lcx.uc[3] = 0 ;} \
+ else { lcx.uc[2] = 255; lcx.uc[3] = 255; }
+
+#define three_bytes_u lcx.uc[3] = getc(dvifile); lcx.uc[1] = getc(dvifile); \
+ lcx.uc[0] = getc(dvifile); lcx.uc[2] = 0
+
+#define three_bytes_s lcx.c[2] = getc(dvifile); lcx.c[1] = getc(dvifile); \
+ lcx.c[0] = getc(dvifile); \
+ lcx.uc[3] = (lcx.c[2] >= 0) ? 0 : 255;
+
+#define four_bytes lcx.c[3] = getc(dvifile); \
+ lcx.c[2] = getc(dvifile); lcx.c[1] = getc(dvifile); \
+ lcx.c[0] = getc(dvifile);
+
+/* Knuth's programs like to hardcode fixed maximum sizes for various
+things, for example, the maximum number of fonts allowed in a DVI file. In
+general, it is preferable to use the C function malloc to allocate storage
+as needed. That is what we generally do in this Dvi2ln3, but there are some
+residues of the Knuthian approach, like MAXTEXFONTS below. By the way, we
+never attempt to return any storage to the system.
+
+[[Eventually, it would be better to get rid of MAXTEXFONTS and use a linked
+list of records instead. There would be no cpu time penalty to using a
+linked list, because the function set_curf below does a linear search
+through the font array anyway.]]
+
+Txf is a data structure describing a TeX font. */
+
+#define MAXTEXFONTS 100
+
+struct txf { unsigned char chu[256]; int bc, ec; long int space, design_size,
+ scaled_size; int nchs; };
+
+GLOBAL struct txf *txfa[MAXTEXFONTS+1];
+
+/* Font_name points to strings containing TeX font names. Those strings are
+created with malloc as needed. */
+
+GLOBAL char *font_name[MAXTEXFONTS+1];
+
+/* Font width information needs to be read from TFM files. TFM is a special
+format defined by TeX. Dvi2ln3 stores each width in a longword. DVItype
+tries to save space by a two-level width storage method. We just allocate
+an array of widths for each font with malloc.
+
+[[We don't check that font checksums match in Dvi2ln3, because there are a
+lot of slightly obsolete TFMs floating around which would result in a
+checksum error, but seem to give perfectly reasonable formatted output
+nonetheless.]] */
+
+GLOBAL long int *font_width[MAXTEXFONTS+1];
+
+/* TeX fonts are referred to by their internal numbers, which go from 0 to
+nf-1. The DVI file refers to them by external numbers, hence the array
+to_ext used to convert internal numbers to external numbers. Curf is the
+internal font number of the current font in the DVI file. */
+
+GLOBAL int to_ext[MAXTEXFONTS+1],
+ nf,
+ curf;
+
+/* In some switch statements, a lot of cases have to be enumerated. We
+employ the following Knuthian macro for that purpose: */
+
+#define four_cases(_x1) case _x1: case _x1+1: case _x1+2: case _x1+3:
+
+/* We define a lot of constants corresponding to the DVI operation codes.
+These are copied from DVItype. */
+
+#define id_byte 2 /* current version of the dvi format */
+#define set_char_0 0 /* typeset character 0 and move right */
+#define set1 128 /* typeset a character and move right */
+#define set_rule 132 /* typeset a rule and move right */
+#define put1 133 /* typeset a character */
+#define put_rule 137 /* typeset a rule */
+#define nop 138 /* no operation */
+#define bop 139 /* beginning of page */
+#define eop 140 /* ending of page */
+#define push 141 /* save the current positions */
+#define pop 142 /* restore previous positions */
+#define right1 143 /* move right */
+#define w0 147 /* move right by |w| */
+#define w1 148 /* move right and set |w| */
+#define x0 152 /* move right by |x| */
+#define x1 153 /* move right and set |x| */
+#define down1 157 /* move down */
+#define y0 161 /* move down by |y| */
+#define y1 162 /* move down and set |y| */
+#define z0 166 /* move down by |z| */
+#define z1 167 /* move down and set |z| */
+#define fnt_num_0 171 /* set current font to 0 */
+#define fnt1 235 /* set current font */
+#define xxx1 239 /* extension to dvi primitives (\special) */
+#define xxx4 242 /* potentially long extension to dvi primitives */
+#define fnt_def1 243 /* define the meaning of a font number */
+#define pre 247 /* preamble */
+#define post 248 /* postamble beginning */
+#define post_post 249 /* postamble ending */
+#define undefined_command 250
+
+GLOBAL long int mag, num, den;
+GLOBAL float conv, unmag_conv;
+
+/* Read_preamble reads the preamble of the dvi file. The comment is thrown
+away. The format version number is checked. The magnification, numerator,
+and denominator are remembered in the globals mag, num, den. The float
+values conv and unmag_conv serve to convert measurements from DVI units to
+pixels. */
+
+int read_preamble() {
+ unsigned int i;
+ int j;
+
+ i = getc(dvifile);
+ if (i != pre) return(1);
+ i = getc(dvifile);
+ if (i != id_byte) return(1);
+ four_bytes; num = lcx.l;
+ if (num <= 0) return(1);
+ four_bytes; den = lcx.l;
+ if (den <= 0) return(1);
+ four_bytes; mag = lcx.l;
+ if (mag <= 0) return(1);
+
+ unmag_conv = (num/254000.0) * (300.0/den);
+ conv = unmag_conv * (mag/1000.0);
+
+/* Skip over the comment field. */
+
+ i = getc(dvifile);
+ for (j=0; j<i; j++) getc(dvifile);
+ return(0);
+}
+
+/* The dvi file format requires us to keep track of various quantities,
+among them the horizontal and vertical positions h and v, and four values
+referred to simply as x, y, z and w. We also keep pixel-rounded versions
+of h and v, called hh and vv. */
+
+GLOBAL long h, v, x, y, z, w;
+GLOBAL int hh, vv;
+
+/* The following function, copied from DVItype, reads from the DVI file the
+first parameter of the current DVI command. In the case of functions whose
+first parameter is implicit, such as set_char_0 through set_char_127, the
+implicit parameter is returned. */
+
+long first_par(o)
+int o;
+{
+ unsigned int i;
+
+ if ((o >= set_char_0) && (o < set_char_0+128))
+ return(o-set_char_0);
+ if ((o >= fnt_num_0) && (o < fnt_num_0+64))
+ return(o-fnt_num_0);
+ switch (o) {
+ case set1:
+ case put1:
+ case fnt1:
+ case xxx1:
+ case fnt_def1:
+ i = getc(dvifile);
+ return(i);
+ case set1+1:
+ case put1+1:
+ case fnt1+1:
+ case xxx1+1:
+ case fnt_def1+1:
+ two_bytes_u;
+ return(lcx.ul);
+ case set1+2:
+ case put1+2:
+ case fnt1+2:
+ case xxx1+2:
+ case fnt_def1+2:
+ three_bytes_u;
+ return(lcx.ul);
+ case right1:
+ case w1:
+ case x1:
+ case down1:
+ case y1:
+ case z1:
+ return(getc(dvifile));
+ case right1+1:
+ case w1+1:
+ case x1+1:
+ case down1+1:
+ case y1+1:
+ case z1+1:
+ two_bytes_s;
+ return(lcx.l);
+ case right1+2:
+ case w1+2:
+ case x1+2:
+ case down1+2:
+ case y1+2:
+ case z1+2:
+ three_bytes_s;
+ return(lcx.l);
+ case set_rule:
+ case put_rule:
+ case right1+3:
+ case w1+3:
+ case x1+3:
+ case down1+3:
+ case y1+3:
+ case z1+3:
+ case set1+3:
+ case put1+3:
+ case fnt1+3:
+ case xxx1+3:
+ case fnt_def1+3:
+ four_bytes;
+ return(lcx.l);
+ case w0:
+ return(w);
+ case x0:
+ return(x);
+ case y0:
+ return(y);
+ case z0:
+ return(z);
+ default:
+ return(0);
+ }
+}
+
+/* Pass1 reads from the DVI file until the last page that has to be
+processed, as determined by the page selection data structures. Font
+definitions are processed, as are font selections; after the starting page
+begins, pass1 records which characters are used from which fonts. Other
+commands are ignored in pass1. Return 1 if something goes wrong, e.g. if
+the DVI file comes to a premature end. This resembles skip_pages below. */
+
+int pass1() {
+
+ unsigned int k;
+ long int p;
+ int i;
+ char startp;
+
+ if (read_preamble() != 0) {
+ printf(bad_DVI_message);
+ return(1);
+ }
+
+ for (;;) {
+ if (feof(dvifile)) {
+ printf(bad_DVI_message);
+ return(1);
+ }
+ k = getc(dvifile);
+ if (k == post) return(0);
+ p = first_par(k);
+ if (k >= set_char_0 && k < set_char_0+128) k = set1;
+ if (k >= fnt_num_0 && k < fnt_num_0+64) k = fnt1;
+
+ switch (k) {
+ case bop:
+ startp = 1;
+ for (i=0; i<10; i++) {
+ four_bytes;
+ if (i < how_many_counts && use_count[i] &&
+ lcx.l != start_page[i]) startp = 0;
+ }
+ four_bytes;
+ if (startp != 0) num_pages = 1;
+ else if (num_pages > 0) {
+ num_pages++;
+ if (num_pages > max_pages) return(0);
+ }
+ break;
+ case set_rule:
+ case put_rule:
+ four_bytes;
+ break;
+ four_cases(fnt_def1)
+ i = define_font(p);
+ if (i != 0) return(1);
+ break;
+ four_cases(fnt1)
+ if (num_pages > 0) set_curf(p);
+ break;
+ four_cases(set1)
+ four_cases(put1)
+ if (num_pages > 0)
+ txfa[curf] -> chu[p] = 1;
+ break;
+
+/* \special's are currently ignored in pass1, and handled only in pass2.
+[[This will change if the putchar special is implemented.]] */
+
+ four_cases(xxx1)
+ for (; p>0; p--) getc(dvifile);
+ break;
+ default:
+ break;
+ }
+ }
+/* return(0); /* Unreachable */
+}
+
+/* FONT LOAD BUILDING: The hardest thing Dvi2ln3 has to do is to build an
+LN03 font load that contains the glyphs required to print the DVI file. The
+following code pertains to that effort.
+
+LN03 fonts come in two flavors. Left fonts are invoked by character codes
+33 to 126. Right fonts are invoked by character codes 161 to 254. Hence: */
+
+#define leftfirst 33
+#define rightfirst 161
+#define leftlast 126
+#define rightlast 254
+
+/* It is believed that there is a maximum of 32 downline-loaded LN03 fonts.
+We always deal with these in pairs, using one as a left font, the other as
+a right font. Hence we have a maximum of 16 pairs. */
+
+#define maxnfonts 16
+
+/* We keep track of the true last character in each LN03 font pair; the
+width of each character; and the LN03 name assigned to that pair. */
+
+GLOBAL int lastch[maxnfonts];
+GLOBAL unsigned char chw[maxnfonts][256];
+GLOBAL char fname[maxnfonts][32];
+
+/* Txf is a record structure describing a TeX font. Txfa is an array of txf
+records. Txf2lnf describes the mapping between TeX fonts and pairs of LN03
+fonts. Because the constructed LN03 font load has just the glyphs that are
+needed, we can often cram more than one TeX font into a pair of LN03 fonts.
+*/
+
+GLOBAL int txf2lnf[MAXTEXFONTS];
+
+/* Maxfontnos reflects the fact that LN03 fonts must be denoted by a number
+from 10 to 19 in order to be selected as the LN03's current font. */
+
+#define maxfontnos 9
+
+/* The LN03 font-denoting numbers 10-19 have to be allocated among the
+fonts. The useno array keeps track of which number a font is using, -1 if
+the font currently doesn't have any number. The whouses array says which
+font is using a given number. */
+
+GLOBAL int useno[maxnfonts],whouses[maxfontnos];
+
+/* After the font load we declare the right and bottom margins we are going
+to use; these are always the maximum allowed values. */
+
+GLOBAL int maxrmar,maxbmar;
+
+/* The global ras_len_added is employed by the font-load-creating code to
+communicate how many bytes of raster information it adds to the font load
+each time it is called. [[Maybe there should be no EXTERNs in the main code
+file, just GLOBALs?]] */
+
+EXTERN long ras_len_added;
+
+#define max(x,y) (((x)>(y))?(x):(y))
+#define min(x,y) (((x)<(y))?(x):(y))
+
+/* Make_font_load calls on other procedures to generate the LN03 font load.
+It writes the opening lines of the output file, too. */
+
+int make_font_load() {
+
+ int i,j,jj,k,l,fno,maxfno,chsize;
+ long int totsize;
+ int lnfcnt,txfcnt,the_txf,txf_size,lnfleft;
+ int txford[MAXTEXFONTS];
+ char cnt[3];
+
+ totsize = 0;
+ chsize = 0;
+ curf = 0;
+ for (i=0; i<maxnfonts; i++)
+ for (j=0; j<256; j++) chw[i][j] = 0;
+
+ for (jj=0; jj<MAXTEXFONTS; jj++) txf2lnf[jj] = -1;
+
+/* Fill the fname strings with different valid LN03 font names. [[This
+initialization could be done statically, but then maxnfonts would be more
+hardcoded.]] */
+
+ for (i=0; i<maxnfonts; i++)
+ strcpy(fname[i],"U000000002SK00GG0001UZZZZ02F000");
+ for (jj=1; jj<maxnfonts; jj++) {
+ sprintf(cnt,"%02d",jj);
+ strncpy(&(fname[jj][5]),cnt,2);
+ }
+
+/* The margins are set to values that seem appropriate for American 8 1/2
+by 11" paper. It is not clear if this needs to be changed for European A4
+paper. Not changing it might deprive Europeans of access to the bottom
+1.5cm of their paper.
+
+It is not clear what the printable area is, or what happens when it is
+exceeded. */
+
+ if (landscape == 1) {
+ maxrmar = 3300;
+ maxbmar = 2475;
+ } else {
+ maxrmar = 2550;
+ maxbmar = 3300;
+ }
+
+/* In the following, esc[?21 J means print in landscape, esc[?27h means
+"advance the carriage by the character width when you set a character",
+esc[11h and esc[7 I together mean to interpret all dimensions in escape
+sequences as pixel units; esc[?52h means our origin of coordinates is the
+upper left edge of the paper; esc[%dt means the "maximum length" of the
+paper is maxbmar pixels. */
+
+ fprintf(outfile,"\033c");
+ if (landscape == 1) fprintf(outfile,"\033[?21 J");
+ fprintf(outfile,"\033[?27h\033[11h\033[7 I\033[?52h\033[%dt\n",
+ maxbmar);
+
+ for (i=0; i<maxnfonts; i++) useno[i] = -1;
+ for (i=0; i<maxfontnos; i++) whouses[i] = -1;
+ maxfno = -1;
+
+/* Write font loading escape sequence onto outf. The ;0y causes all
+previously downloaded fonts to be cleared from the LN03's font memory. */
+
+ fprintf(outfile,"\033P0;1;0y");
+
+/* Count how many glyphs each font requires. */
+
+ for (fno=0; fno<nf; fno++) {
+
+ txfa[fno] -> nchs = 0;
+ for(i=0; i<256; i++)
+ if (txfa[fno] -> chu[i] != 0) txfa[fno] -> nchs++;
+
+ if (txfa[fno] -> nchs > 188) goto need_empty_slots;
+ chsize += txfa[fno] -> nchs;
+ }
+ printf("\nFont load to contain %d glyphs.",chsize);
+
+
+/* Now we have to allocate TeX fonts to LN03 font pairs and perform the
+actual load. The goal is to use as few LN03 fonts as possible.
+
+I don't know any way to do the allocation optimally, so a first-fit
+heuristic is used.
+
+1. Find the largest unassigned TeX font. If none, exit, we're done.
+ Number of glyphs used is the measure of size.
+
+2. Allocate an LN03 font pair for that TeX font and put the TeX
+ font into it.
+
+3. Find the largest remaining TeX font that fits in what is left of
+ that LN03 font pair.
+
+ 3.1. if none exists, go to 1.
+ 3.2. if one exists, put it in the font pair and go back to 3.
+
+The txford array contains the TeX font numbers in the order that they are
+assigned. Txfcnt keeps track of how many TeX fonts have been assigned so
+far. */
+
+ lnfcnt = 0;
+ txfcnt = 0;
+ for (i=0; i<MAXTEXFONTS; i++) txford[i] = -1;
+
+ while (1) {
+
+/* Find largest unassigned TeX font */
+
+ lnfleft = rightlast-rightfirst+1+leftlast-leftfirst+1;
+ txf_size = -1;
+ for (j = 0; j < MAXTEXFONTS; j++) {
+ if (txfa[j] != 0 && txf2lnf[j] == -1
+ && txfa[j] -> nchs > txf_size) {
+ txf_size = txfa[j] -> nchs;
+ the_txf = j;
+ }
+ }
+ if (txf_size <= 0) goto assignment_done;
+ if (lnfcnt > maxnfonts) goto too_complex;
+ if (txf_size > lnfleft) goto need_empty_slots;
+
+/* Now allocate new LN03 font pair and put the current TeX font into it */
+
+ txf2lnf[the_txf] = lnfcnt;
+ txford[txfcnt] = the_txf;
+ txfcnt++;
+ k = leftfirst-1;
+ for (j = 0; j <= 255; j++) {
+ if (txfa[the_txf] -> chu[j] != 0) {
+ k++;
+ if (k == leftlast+1) k = rightfirst;
+ lnfleft--;
+ txfa[the_txf] -> chu[j] = k;
+ }
+ }
+ lastch[lnfcnt] = k;
+
+/* Now try to fill the remaining part of the LN03 font pair using other TeX
+fonts */
+
+ while (1) {
+
+ txf_size = -1;
+ for (j = 0; j < MAXTEXFONTS; j++) {
+ if (txfa[j] != 0 && txf2lnf[j] == -1
+ && txfa[j] -> nchs > txf_size
+ && txfa[j] -> nchs <= lnfleft) {
+ txf_size = txfa[j] -> nchs;
+ the_txf = j;
+ }
+ }
+ if (txf_size <= 0) break;
+
+ txf2lnf[the_txf] = lnfcnt;
+ txford[txfcnt] = the_txf;
+ txfcnt++;
+ k = lastch[lnfcnt];
+ for (j=0; j<=255; j++) {
+ if (txfa[the_txf] -> chu[j] != 0) {
+ k++;
+ lnfleft--;
+ if (k == leftlast+1) k = rightfirst;
+ txfa[the_txf] -> chu[j] = k;
+ }
+ }
+ lastch[lnfcnt] = k;
+ }
+
+ lnfcnt++;
+ }
+
+assignment_done:
+
+/* At this point, the TeX fonts have been assigned to LN03 font pairs. The
+assignment is reflected in the arrays txf2lnf, lastch and txford. It
+remains to actually generate the desired font load. This has to be done
+carefully, since the function add_txf_to_lnf only supports adding glyphs to
+an LN03 font pair in ascending order of character code. */
+
+ for (j=0; j<txfcnt; j++) {
+ k = txford[j];
+ if (j != 0 && txf2lnf[k] != txf2lnf[txford[j-1]])
+ fprintf(outfile,",\n");
+ add_txf_to_lnf(txf2lnf[k],k);
+ totsize += ras_len_added;
+ }
+
+/* [[At this point we ought to add code that writes a message saying how
+much font RAM the load will occupy. But there is no documentation for
+determining that! So, we make the following compromise:]] */
+
+ printf("\n Approximate size of font load: %ld bytes",totsize);
+
+/* Now output escape sequences to the LN03 that assign the available font
+numbers to the first ten font pairs in the load. [[We may want to insert a
+few \n's in the following to keep the line from getting too long.]] */
+
+ fprintf(outfile,"\n;Dvi2ln3 9 font load\033\\");
+ fprintf(outfile,"\033[1;%ds\033[%dt\033[1;%dr",maxrmar,maxbmar,maxbmar);
+ for (j=0; j <= min(maxfontnos,lnfcnt-1); j++) {
+ useno[j] = j;
+ whouses[j] = j;
+ k = fname[j][16];
+ fname[j][16] = '\0';
+ fprintf(outfile,"\033P1;1%d}%s\033\\",j,fname[j]);
+ fname[j][16] = k;
+ }
+ fprintf(outfile,"\033[10m");
+
+ return(0);
+
+too_complex:
+ printf("\n Can't construct a font load:");
+ printf("\n Dvi file uses too many glyphs from too many different fonts.");
+ return(1);
+
+/* [[We need to determine what the real limit is on the number of LN03
+fonts, and refine the test to detect if it is being exceeded. Fortunately,
+only a very complex DVI file could exceed the limit.]] */
+
+need_empty_slots:
+ printf("\nFont %s uses > 188 characters. Can't handle that.",
+ font_name[fno]);
+ return(1);
+
+/* [[The error message above should be fixed to specify the magnification
+of the font also.]] */
+
+}
+
+/* The lines in the LN3 file are limited to lengths of about 100 bytes. We
+keep track of how many bytes are written so far in the global lnhp. The
+accounting is conservative, so e.g. each integer representing a pixel
+position counts as 4 bytes even though it might be shorter. [[One could
+easily increase 100 to 200. It is not clear what problems are provoked by
+going over, say, 256.]]
+
+Vpset keeps track of whether the vertical position needs to be output to
+the LN03 file before setting any more characters. Hh_old keeps track of the
+horizontal position which LN03 thinks it's at. */
+
+GLOBAL int ln3p,vpset,hh_old;
+
+#define inc_ln3p(x) ln3p += x; if (ln3p > 100) \
+ { ln3p = 0; vpset = 0; hh_old = 30000; }
+
+/* [[In the above macro, hh_old is set to 30000 to mean "a very large
+number" (the maximum valid hh for an LN03 is 2550), so as to force the code
+below to re-output the true hh. What are the implications of this kludge?
+Would it not be more reasonable to have instead an hhset variable?]] */
+
+/* A stack of hvxyzw values is kept; the stack pointer is called s.
+[[Again, it would be better to make this stack a linked list.]] */
+
+#define STACKSIZE 100
+
+GLOBAL long hstack[STACKSIZE], vstack[STACKSIZE], xstack[STACKSIZE],
+ ystack[STACKSIZE], zstack[STACKSIZE], wstack[STACKSIZE];
+GLOBAL int hhstack[STACKSIZE], vvstack[STACKSIZE];
+GLOBAL int s;
+
+/* DVI files specify distances in units of 2^-16 points. When translating
+to a device-specific format, it is necessary to round the DVI distances to
+pixel units. This is done by means of the pixel_round macro.
+
+However, rather than using this macro in the straightforward way, rounding
+is often performed by more elaborate techniques, which we call "Stanford
+rules" (after John Le Carre's "Moscow rules"). These rules make use of a
+parameter MAX_DRIFT, which is roughly the maximum number of pixels that
+things are allowed to deviate from straightforward rounding. */
+
+#define pixel_round(x) ((int) ((x<0) ? \
+ (conv*(x)-0.5) : (conv*(x)+0.5)))
+
+#define MAX_DRIFT 2
+
+/* Pass2 reads the dvi file and interprets the commands in it, usually by
+calling other routines. The interpretation generally consists of writing
+something into the output file, updating the current fonts and positions,
+and possibly updating the stack. */
+
+int pass2()
+{
+ int i,j;
+ long int p;
+ unsigned int k;
+
+ if (read_preamble() != 0) {
+ printf(bad_DVI_message);
+ return(1);
+ }
+
+ curf = 0;
+ ln3p = 0;
+ s = 0;
+ vpset = 0;
+ hh_old = 30000;
+
+/* Skip pages until the desired starting page is reached. A nonzero value
+is returned if the starting page is never encountered. */
+
+ if (skip_pages() != 0) return(0);
+
+ for (;;) {
+ if feof(dvifile) {
+ printf(bad_DVI_message);
+ return(1); }
+ k = getc(dvifile);
+ if (k == post) return(0);
+ if (k >= undefined_command) {
+ printf(bad_DVI_message);
+ return(1); }
+
+ p = first_par(k);
+
+/* If the opcode is an "implicit parameter" opcode, either set_char_n and
+fnt_num_n, we change it to an explicit parameter opcode to make the case
+jumps more reasonable. */
+
+ if (k >= set_char_0 && k < set_char_0+128) k = set1;
+ if (k >= fnt_num_0 && k < fnt_num_0+64) k = fnt1;
+
+ j = do_command(k,p);
+ if (j == 2) return(0); /* done with the required number of pages */
+ else if (j == 1) return(1); /* error encountered, stop */
+
+ }
+}
+
+GLOBAL long first_counter;
+
+/* Read from the DVI file until the starting page condition is met. Return
+1 if something goes wrong, or if the DVI file comes to an end. This
+function is copied quite closely from DVItype. */
+
+int skip_pages() {
+
+ unsigned int k;
+ int i;
+ long p;
+ char startp;
+
+ for (;;) {
+ if (feof(dvifile)) return(1);
+ k = getc(dvifile);
+ if (k == post) return(1);
+ p = first_par(k);
+ if (k >= set_char_0 && k < set_char_0+128) k = set1;
+ if (k >= fnt_num_0 && k < fnt_num_0+64) k = fnt1;
+
+ switch (k) {
+ case bop:
+ startp = 1;
+ for (i=0; i<10; i++) {
+ four_bytes;
+ if (i == 0) first_counter = lcx.l;
+ if (i < how_many_counts && use_count[i] &&
+ lcx.l != start_page[i]) startp = 0;
+ }
+ four_bytes;
+ if (startp != 0) {
+ v = 0; vv = 0; h = 0; hh = 0;
+ printf("\n [%ld]",first_counter);
+ num_pages = 1; return(0); }
+ break;
+ case set_rule:
+ case put_rule:
+ four_bytes;
+ break;
+ four_cases(fnt_def1)
+
+/* [[Why are we defining fonts again here? Once, in pass1, ought to have
+been enough...]] */
+
+ i = define_font(p);
+ if (i != 0) return(1);
+ break;
+ four_cases(xxx1)
+ for (; p>0; p--) getc(dvifile);
+ break;
+ default:
+ break;
+ }
+ }
+/* return(0); /* Unreachable */
+}
+
+/* Do_command performs the DVI command of code k, assuming the "first
+parameter" is p. It is assumed that k is not one of set_char0 through
+set_char127. */
+
+int do_command(k,p)
+int k;
+long p;
+{
+ int i,j,l,lnf;
+
+ switch (k) {
+ four_cases(fnt_def1)
+ define_font_pass2();
+ break;
+
+/* It is possible that the dvi file sets some glyphs off the page. No error
+message is given, since the user will generally see what is happening in
+the output (plus, if Dvi2ln3 gives an error message, users will complain
+about Dvi2ln3 even though the error is in their TeX file).
+
+In most cases, dvi2ln3 takes no special action at all, since the LN03 will
+clip the glyphs for us. Unfortunately, the LN03 doesn't let us specify
+negative horizontal or vertical positions, so we have to clip glyphs at
+such positions away ourselves. Also, if one sets at a position below the
+bottom margin, the LN03 will eject a page, so glyphs that are set below
+that margin also have to be clipped by hand.
+
+The following code accomplishes that: */
+
+ four_cases(put1)
+ four_cases(set1)
+
+ if (vv+voff > 0 && vv+voff <= maxbmar && hh+hoff > 0) {
+ if (!vpset) {
+ fprintf(outfile,"\n\033[%dd",vv+voff);
+ fprintf(outfile,"\033[%d`",hh+hoff);
+ ln3p = 16;
+ vpset = 1;
+ hh_old = hh;
+ }
+ if (hh_old != hh) {
+ if (hh > hh_old) fprintf(outfile,"\033[%da",hh-hh_old);
+ else fprintf(outfile,"\033[%d`",hh+hoff);
+ ln3p += 7;
+ }
+
+#ifndef SEVENBIT
+ putc(txfa[curf] -> chu[p],outfile);
+#else
+ if (txfa[curf] -> chu[p] > 127) {
+ if (right7 == 0) {
+ fputs("\033n",outfile);
+ inc_ln3p(2);
+ right7 = 1;
+ }
+ putc(txfa[curf] -> chu[p]-128,outfile);
+ } else {
+ if (right7 == 1) {
+ putc(15,outfile);
+ inc_ln3p(1);
+ right7 = 0;
+ }
+ putc(txfa[curf] -> chu[p],outfile);
+ }
+#endif
+ inc_ln3p(1);
+ }
+ if (k >= put1) {
+ hh_old = hh+chw[txf2lnf[curf]][txfa[curf] -> chu[p]];
+ break;
+ }
+ h += font_width[curf][p];
+
+/* In rounding h to generate the pixel-position hh, Stanford rules (see
+above) come into play. We set the new hh (horizontal position in pixels) to
+the value obtained by adding the pixel width of the character being set to
+the current position. We then correct this value so that it does not exceed
+the rounded version of the true position by more than MAX_DRIFT pixels.
+
+Note that if we did not apply Stanford rules here, or equivalently if we
+set MAX_DRIFT to zero, many more set-X-position commands would appear in
+the output. */
+
+ hh += chw[txf2lnf[curf]][txfa[curf] -> chu[p]];
+ hh_old = hh;
+ l = pixel_round(h);
+ if (hh-l > MAX_DRIFT) hh = l+MAX_DRIFT;
+ else if (l-hh > MAX_DRIFT) hh = l-MAX_DRIFT;
+ break;
+
+ four_cases(fnt1)
+ set_curf(p);
+ lnf = txf2lnf[curf];
+ if (useno[lnf] == -1) {
+ useno[whouses[maxfontnos]] = -1;
+ useno[lnf] = maxfontnos;
+ whouses[maxfontnos] = lnf;
+ fprintf(outfile,"\033P1;1%d}%16s\033\\",maxfontnos,
+ fname[lnf]);
+ inc_ln3p(26);
+ }
+ fprintf(outfile,"\033[1%dm",useno[lnf]);
+ inc_ln3p(5);
+ break;
+
+ case set_rule:
+ case put_rule:
+
+/* When converting rule dimensions to pixel dimensions, we do not follow
+Stanford rules. Rather, we just round the true positions to obtain the
+pixel positions. This avoids unsightly gaps between rules. [[It should not
+cause much of a problem with typical rule applications (ruled tables,
+fraction bars)...but perhaps with large delimiters there might be some
+difficulty. This needs more thought...]] */
+
+ four_bytes;
+ if (p >= 0 && lcx.l >= 0)
+ do_rule(pixel_round(h),
+ pixel_round(v-p),pixel_round(h+lcx.l),
+ pixel_round(v));
+ if (k == set_rule) {
+ h += lcx.l;
+ hh = pixel_round(h);
+ }
+ break;
+
+ case push:
+ s++;
+ if (s == STACKSIZE) {
+ printf("\n Stack too deep for Dvi2ln3");
+ return(1);
+ }
+ xstack[s] = x;
+ ystack[s] = y;
+ vstack[s] = v;
+ hstack[s] = h;
+ vvstack[s] = vv;
+ hhstack[s] = hh;
+ wstack[s] = w;
+ zstack[s] = z;
+ break;
+
+ case pop:
+ if (s == 0) {
+ printf(bad_DVI_message);
+ return(1);
+ }
+ if (vv != vvstack[s]) vpset = 0;
+ x = xstack[s];
+ y = ystack[s];
+ v = vstack[s];
+ h = hstack[s];
+ vv = vvstack[s];
+ hh = hhstack[s];
+ w = wstack[s];
+ z = zstack[s];
+ s--;
+ break;
+
+ four_cases(xxx1)
+ do_special_pass2(p);
+ break;
+
+ case bop:
+
+/* If we've done the required number of pages, we'll skip the rest of the
+DVI file. If not, type the first parameter of bop on the user's terminal
+the way TeX does, to give an indication of progress. */
+
+ if (num_pages == max_pages) return(2);
+ v = 0; vv = 0; h = 0; hh = 0;
+ vpset = 0; hh_old = 100000;
+ four_bytes;
+ if (num_pages%12 == 0) printf("\n");
+ first_counter = lcx.l;
+ printf(" [%ld]",lcx.l);
+ num_pages++;
+ for (i = 0; i<40; i++) getc(dvifile);
+ break;
+
+ case eop:
+ fprintf(outfile,"\n\f");
+ ln3p = 0;
+ break;
+
+/* Now we have to consider the cases for pure motion. */
+
+ four_cases(right1)
+ set_h(h+p);
+ break;
+ four_cases(x1)
+ x = p;
+ case x0:
+ set_h(h+x);
+ break;
+ four_cases(y1)
+ y = p;
+ case y0:
+ set_v(v+y);
+ break;
+ four_cases(w1)
+ w = p;
+ case w0:
+ set_h(h+w);
+ break;
+ four_cases(z1)
+ z = p;
+ case z0:
+ set_v(v+z);
+ break;
+ four_cases(down1)
+ set_v(v+p);
+ break;
+
+ }
+ return(0);
+}
+
+/* Set_curf sets the current font to external number p. Note that the
+current font is maintained as an internal font number. */
+
+int set_curf(p)
+int p;
+{
+ to_ext[nf] = p;
+ curf = 0;
+ while (to_ext[curf] != p) { curf++; }
+}
+
+/* Define_font processes a font definition from the DVI file. The TFM file
+for the font is read at this point from the directory TEX$FONTS. */
+
+int define_font(e)
+int e;
+{
+ int i;
+ unsigned char p,n;
+ if (e == MAXTEXFONTS) { printf("\n Too many fonts for Dvi2ln3");
+ return(1); }
+ txfa[nf] = (struct txf *)malloc(sizeof(struct txf));
+ to_ext[nf] = e;
+ four_bytes;
+ four_bytes;
+ txfa[nf] -> scaled_size = lcx.l;
+ four_bytes;
+ txfa[nf] -> design_size = lcx.l;
+ for (i=0; i<256; i++) txfa[nf] -> chu[i] = '\0';
+ p = getc(dvifile); n = getc(dvifile);
+ font_name[nf] = malloc(p+n+1);
+ for (i=0; i<p+n; i++) { font_name[nf][i] = getc(dvifile); }
+ font_name[nf][p+n] = '\0';
+
+ if (txfa[nf] -> scaled_size <= 0 || txfa[nf] -> scaled_size >=
+ 8*8*8*8*8*8*8*8*8) {
+ printf("\n Font %s has a bad scaled size",font_name[nf]);
+ return(1);
+ }
+ if (txfa[nf] -> design_size <= 0 || txfa[nf] -> design_size >=
+ 8*8*8*8*8*8*8*8*8) {
+ printf("\n Font %s has a bad design size",font_name[nf]);
+ return(1);
+ }
+
+/* We follow DVItype and compute for each font a "space" parameter which is
+one-sixth of the scaled design size. This parameter is used in rounding the
+horizontal position to pixels according to "Stanford rules." See the
+function set_h below. */
+
+ txfa[nf] -> space = txfa[nf] -> scaled_size/6;
+
+ i = read_tfm_file();
+ if (i != 0) return(i);
+ nf++;
+ return(0);
+
+}
+
+/* TFM files, like the DVI file, are read with getc, foolish as that may
+seem. We use the same overlays that are used for merging DVI bytes into
+longwords. However, since the TFM file consists of longwords, only
+tfm_longword is need. [[This is also VAX dependent. It would not be
+possible to use stdio's longword reading function getw here, because the
+bytes have to be reversed.]] */
+
+#define tfm_longword { lcx.uc[3] = getc(tfmfile); \
+ lcx.uc[2] = getc(tfmfile); lcx.uc[1] = getc(tfmfile); \
+ lcx.uc[0] = getc(tfmfile); }
+
+/* Read_tfm_file obtains the character widths from the TFM file
+corresponding to font nf. */
+
+int read_tfm_file() {
+
+ int i,lh,nw;
+ int info[256];
+ long z,alpha,beta;
+ long width[256];
+
+ open_tfm_file();
+ if (tfmfile == NULL) {
+ printf("\n Can't open TFM file for font %s",font_name[nf]);
+ return(1); }
+
+/* The TFM format is described in some issue of the TUGBoat (TeX users'
+group newsletter), and in the comments to the program TFtoPL. Here we
+summarize those aspects of TFM format that are relevant to the task of
+extracting the widths of the characters.
+
+The first 24 bytes of a TFM file contain twelve 16-bit integers that give
+the lengths of the various subsequent portions of the file. The ones
+relevant to our purposes are LH, length of the header data, BC, the
+smallest character code in the font, EC, the largest character code in the
+font, and NW, number of words in the width table. So, we read those right
+now, and then skip over the remainder, and then over a set of LH longwords
+called the "header." */
+
+ tfm_longword;
+ lh = 256*lcx.uc[1] + lcx.uc[0];
+ tfm_longword;
+ txfa[nf] -> bc = 256*lcx.uc[3] + lcx.uc[2];
+ txfa[nf] -> ec = 256*lcx.uc[1] + lcx.uc[0];
+ tfm_longword;
+ nw = 256*lcx.uc[3] + lcx.uc[2];
+ if (txfa[nf] -> bc > 255 || txfa[nf] -> ec > 255 ||
+ txfa[nf] -> bc > txfa[nf] -> ec || nw > 256) {
+ printf("\n Bad TFM file for font %s",font_name[nf]);
+ return(1);
+ }
+
+ for(i=0; i<lh+3; i++) tfm_longword;
+
+/* After the header, there are two arrays in the TFM file that interest us.
+The first, INFO, is EC-BC+1 longwords long and contains pointers to the
+second, WIDTH, which is NW longwords long. For each character i, the width
+of i is WIDTH[INFO[i-BC]]. We read these arrays into memory. */
+
+ for(i=0; i<txfa[nf] -> ec-txfa[nf] -> bc+1; i++) {
+ tfm_longword;
+ if (lcx.uc[3] >= nw) {
+ printf("\n Bad TFM file for font %s",font_name[nf]);
+ return(1);
+ }
+ info[i] = lcx.uc[3];
+ }
+
+/* The widths are stored in a rather strange format known as a "fix-word."
+A nonnegative width is expressed in "fix-word" format by expressing it in
+units of 2^-20 times the design size. A negative width is expressed in
+"fix-word" format by expressing its negative in those units, and then
+changing the most significant byte to 255.
+
+One needs to convert these widths into DVI units, multiplying by the scaled
+design size according to a certain arcane algorithm. The algorithm is
+copied from DVItype, to which we refer the reader for an explanation. */
+
+ z = txfa[nf] -> scaled_size;
+ alpha = 16*z; beta = 16;
+ while (z >= 4*(8*8*8*8*8*8*8)) { z = z/2; beta = beta/2; }
+ for(i=0; i<nw; i++) {
+ tfm_longword;
+ width[i] = ( ( (lcx.uc[0]*z)/256 + lcx.uc[1]*z )/256 +
+ lcx.uc[2]*z)/beta;
+ if (lcx.uc[3] == 255) width[i] -= alpha;
+ }
+
+ fclose(tfmfile);
+
+/* Using these two arrays, we now compute the widths of the characters in
+the fonts and place them into two malloc'ed arrays, one for the widths in
+DVI units, one for the width in pixel units. */
+
+ font_width[nf] = (long int *)malloc(4*(txfa[nf] -> ec+1));
+
+ for (i=0; i<txfa[nf] -> bc; i++) font_width[nf][i] = 0;
+ for (i=txfa[nf] -> bc; i <= txfa[nf] -> ec; i++)
+ font_width[nf][i] = width[info[i-txfa[nf] -> bc]];
+ return(0);
+}
+
+/* Open_tfm_file finds and opens the tfm file corresponding to a font nf.
+The global file variable tfmfile is used to hold the file pointer. [[This
+function will not work if the TFM file lies over the net on a VMS V3.x
+host, because C file opens do not work in such circumstances. The problem
+can be ignored, because eventually there should be very few VMS V3.x hosts
+left.]] */
+
+int open_tfm_file () {
+
+ int jext,jnam;
+ char filespec[FILESPECLEN];
+
+#ifndef vms
+ char *texfontdir;
+#endif
+
+
+ find_filename(font_name[nf],&jnam,&jext);
+ filespec[0] = '\0';
+
+/* If there is no directory part, fill in using the logical tex$fonts */
+
+#ifdef vms
+ if (jnam == 0) strcpy(filespec,"tex$fonts:");
+#else
+ if (jnam == 0) {
+ texfontdir = getenv("TEX_FONTS");
+ strcpy(filespec,texfontdir);
+ strcat(filespec,"/");
+ }
+#endif
+ strcat(filespec,font_name[nf]);
+
+/* if there is no extension, add the extension ".tfm" */
+
+ if (font_name[nf][jext] == '\0') strcat(filespec,".tfm");
+ tfmfile = fopen(filespec,"r");
+
+}
+
+/* Define_font_pass2 skips a font definition from the DVI file. */
+
+int define_font_pass2()
+{
+ unsigned char p,n;
+ int i;
+ four_bytes;
+ four_bytes;
+ four_bytes;
+ p = getc(dvifile); n = getc(dvifile);
+ for (i=0; i<p+n; i++) getc(dvifile);
+}
+
+/* Do_rule writes into the ln3 file the escape sequence corresponding to a
+rule. The rule is silently clipped to fit on the page. */
+
+int do_rule(xx0,yy0,xx1,yy1)
+int xx0,yy0,xx1,yy1;
+{
+ int j;
+
+ xx0 = min(xx0+hoff,maxrmar);
+ xx1 = min(xx1+hoff,maxrmar);
+ xx0 = max(xx0,0);
+ xx1 = max(xx1,0);
+ yy0 = min(yy0+voff,maxbmar);
+ yy1 = min(yy1+voff,maxbmar);
+ yy0 = max(yy0,0);
+ yy1 = max(yy1,0);
+ if (xx0 > xx1) { j = xx0; xx0 = xx1; xx1 = j; }
+ if (yy0 > yy1) { j = yy0; yy0 = yy1; yy1 = j; }
+
+ if ((yy1 != yy0 && xx1 != xx0)) {
+ fprintf(outfile,"\033[1;%d;%d;%d;%d!|",
+ xx0,yy0,yy1-yy0,xx1-xx0);
+ inc_ln3p(25);
+ }
+}
+
+/* Set_v is called to execute vertical-position-altering commands, other
+than pops. It modifies v and vv, and outputs the vertical position to the
+LN3 file. We don't follow Stanford rules here. They sometimes set the
+vertical position in pixels vv to something other than the rounded value of
+v. Not following Stanford rules implies, for example, that the relative
+vertical positions of an accent and its accentee may differ by one pixel
+according to how the baseline of the accentee gets rounded. [[This should
+perhaps be fixed.]] */
+
+int set_v(v1)
+int v1;
+{
+ int l;
+
+ l = pixel_round(v1);
+ v = v1;
+ vv = l;
+ vpset = 0;
+}
+
+/* Set_h is called whenever a DVI command is encountered that alters the
+horizonal position, other than a set_char, set_rule or pop. It sets h to
+the new value, and alters hh according to Stanford rules. */
+
+int set_h(new_h)
+int new_h;
+{
+ int l,old_hh;
+
+ old_hh = hh;
+ l = pixel_round(new_h);
+ if (txfa[curf] == 0 || new_h-h >= txfa[curf] -> space
+ || new_h-h <= -4*txfa[curf] -> space) hh = l;
+ else {
+ hh += pixel_round(new_h-h);
+ if (hh-l > MAX_DRIFT) hh = l+MAX_DRIFT;
+ else if (hh-l < -MAX_DRIFT) hh = l-MAX_DRIFT;
+ }
+ h = new_h;
+ return(0);
+}
diff --git a/dviware/ln03/rose/dvi2ln3.dif b/dviware/ln03/rose/dvi2ln3.dif
new file mode 100644
index 0000000000..56d9066f2e
--- /dev/null
+++ b/dviware/ln03/rose/dvi2ln3.dif
@@ -0,0 +1,109 @@
+-----------------------------------------------------------------------------------------------------------------------------------
+File USER$1:[ROSE.TG.C]DVI2LN3.C;48 | File USER$1:[ROSE.TG.UNIX]DVI2LN3.C;1
+-------------------------------- 3 --------------------------------------------------------------- 3 ------------------------------
+Dvi2ln3 is still being developed. Copyright (c) 1985, 1986 by D | Dvi2ln3 is still being developed. Copyright (c) 1985, 1986, 198
+-------------------------------- 62 -------------------------------------------------------------- 62 -----------------------------
+3/4/86: Support for landscape added -- very easy, if only I h |
+ known. Fixed bug found by Mark DeVries, error returne |
+ if \special selects device other than LN03. Version 8 |
+ |
+-------------------------------- 86 -------------------------------------------------------------- 82 -----------------------------
+#endif | char *strchr(), *malloc(), *getenv();
+ | #endif
+/* Here begins a long list of global variables. */ |
+-------------------------------- 120 ------------------------------------------------------------- 115 ----------------------------
+/* landscape says whether we are printing in landscape or portr |
+ |
+GLOBAL char landscape; |
+ |
+-------------------------------- 139 ------------------------------------------------------------- 130 ----------------------------
+ printf("\n Dvi2ln3 8"); | printf("\n Dvi2ln3 7");
+-------------------------------- 151 ------------------------------------------------------------- 142 ----------------------------
+ find_VMS_filename(infnam,&jnam,&jext); | find_ULTRIX_filename(infnam,&jnam,&jext);
+-------------------------------- 163 ------------------------------------------------------------- 154 ----------------------------
+ landscape = 0; |
+-------------------------------- 170 ------------------------------------------------------------- 160 ----------------------------
+ if (rewind(dvifile) == -1) { | rewind(dvifile);
+ printf("\n Couldn't rewind dvi file."); |
+ goto exit_label; |
+ } |
+-------------------------------- 189 ------------------------------------------------------------- 176 ----------------------------
+ ; | printf("\n");
+} | }
+ |
+/* Find_VMS_filename finds the filename part of a VMS filespec | /* find_ULTRIX_filename finds the filename part of a VMS filesp
+-------------------------------- 201 ------------------------------------------------------------- 188 ----------------------------
+int find_VMS_filename(s,ns,ne) | int find_ULTRIX_filename(s,ns,ne)
+-------------------------------- 210 ------------------------------------------------------------- 197 ----------------------------
+ if (s[j] == ':' || s[j] == ']' || | if (s[j] == '/') {
+ s[j] == '>') { |
+-------------------------------- 219 ------------------------------------------------------------- 205 ----------------------------
+ if (s[j] == '.' || s[j] == ';') { | if (s[j] == '.') {
+-------------------------------- 260 ------------------------------------------------------------- 246 ----------------------------
+second identifying longword is 8 and third is 3. | second identifying longword is 8 and third is 3. */
+ |
+The L option says print in landscape. | /* [[The toupper's below cause difficulty under Ultrix. Probabl
+ | can be fixed by #including <ctype.h>.]] */
+[[Is a European option needed?]] */ |
+-------------------------------- 283 ------------------------------------------------------------- 268 ----------------------------
+ if (toupper(t[0]) == 'L') landscape = 1; |
+-------------------------------- 327 ------------------------------------------------------------- 311 ----------------------------
+ jj = creat(outfnam,0); | jj = creat(outfnam, 0700);
+-------------------------------- 749 ------------------------------------------------------------- 733 ----------------------------
+/* The margins are set to values that seem appropriate for Amer | /* The margins are set to values appropriate for American 8 1/2
+by 11" paper. It is not clear if this needs to be changed for E | paper. It is not clear if this needs to be changed for European
+paper. Not changing it might deprive Europeans of access to the | Not changing it might deprive Europeans of access to the bottom
+1.5cm of their paper. | their paper. */
+ |
+It is not clear what the printable area is, or what happens whe | maxrmar = 2550;
+exceeded. */ | maxbmar = 3300;
+ |
+ if (landscape == 1) { | /* In the following, esc[?27h means "advance the carriage by th
+ maxrmar = 3300; | width when you set a character", esc[11h and esc[7 I together m
+ maxbmar = 2475; | interpret all dimensions in escape sequences as pixel units; es
+ } else { | our origin of coordinates is the upper left edge of the paper;
+ maxrmar = 2550; | means the "maximum length" of the paper is maxbmar pixels. */
+ maxbmar = 3300; |
+ } | fprintf(outfile,"\033c\033[?27h\033[11h\033[7 I\033[?52h\03
+ |
+/* In the following, esc[?21 J means print in landscape, esc[?2 |
+"advance the carriage by the character width when you set a cha |
+esc[11h and esc[7 I together mean to interpret all dimensions i |
+sequences as pixel units; esc[?52h means our origin of coordina |
+upper left edge of the paper; esc[%dt means the "maximum length |
+paper is maxbmar pixels. */ |
+ |
+ fprintf(outfile,"\033c"); |
+ if (landscape == 1) fprintf(outfile,"\033[?21 J"); |
+ fprintf(outfile,"\033[?27h\033[11h\033[7 I\033[?52h\033[%dt |
+-------------------------------- 919 ------------------------------------------------------------- 892 ----------------------------
+ fprintf(outfile,"\n;Dvi2ln3 8 font load\033\\"); | fprintf(outfile,"\n;Dvi2ln3 7 font load\033\\");
+-------------------------------- 1127 ------------------------------------------------------------ 1100 ---------------------------
+about Dvi2ln3 even though the error is in their TeX file). | about Dvi2ln3 even though the error is in their TeX file). In m
+ | dvi2ln3 takes no special action at all, since the LN03 will cli
+In most cases, dvi2ln3 takes no special action at all, since th | for us. Unfortunately, the LN03 doesn't let us specify negative
+clip the glyphs for us. Unfortunately, the LN03 doesn't let us | or vertical positions, so we have to clip glyphs at such positi
+negative horizontal or vertical positions, so we have to clip g | ourselves. The following code accomplishes that: */
+such positions away ourselves. Also, if one sets at a position |
+bottom margin, the LN03 will eject a page, so glyphs that are s |
+that margin also have to be clipped by hand. |
+ |
+The following code accomplishes that: */ |
+-------------------------------- 1141 ------------------------------------------------------------ 1109 ---------------------------
+ if (vv+voff > 0 && vv+voff <= maxbmar && hh+hoff > | if (vv+voff > 0 && hh+hoff > 0) {
+-------------------------------- 1481 ------------------------------------------------------------ 1449 ---------------------------
+ | char *texfontdir;
+ find_VMS_filename(font_name[nf],&jnam,&jext); |
+ | find_ULTRIX_filename(font_name[nf],&jnam,&jext);
+-------------------------------- 1487 ------------------------------------------------------------ 1456 ---------------------------
+ if (jnam == 0) strcpy(filespec,"tex$fonts:"); | if (jnam == 0) texfontdir = getenv("TEX_FONTS");
+ | strcpy(filespec,texfontdir);
+ | strcat(filespec,"/");
+-----------------------------------------------------------------------------------------------------------------------------------
+
+Number of difference sections found: 21
+Number of difference records found: 78
+
+DIFFERENCES /IGNORE=()/OUTPUT=USER$1:[ROSE.TG.C]DVI2LN3.DIF;1/PARALLEL-
+ USER$1:[ROSE.TG.C]DVI2LN3.C;48-
+ USER$1:[ROSE.TG.UNIX]DVI2LN3.C;1
diff --git a/dviware/ln03/rose/dvi2ln3.lis b/dviware/ln03/rose/dvi2ln3.lis
new file mode 100644
index 0000000000..b8c209889d
--- /dev/null
+++ b/dviware/ln03/rose/dvi2ln3.lis
@@ -0,0 +1,1602 @@
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 1
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+
+
+ 1 /* Dvi2ln3 translates a TeX DVI file to an LN03 format file.
+ 2
+ 3 Dvi2ln3 is still being developed. Copyright (c) 1985 by Digital Equipment
+ 4 Corporation, Maynard, Massachusetts, USA. Author: Flavio Rose,
+ 5 shasta!decwrl!dec-rhea!dec-dvinci!rose.
+ 6
+ 7 Dvi2ln3 is based on the publicly-available program DVItype, written by
+ 8 David R. Fuchs of Stanford University; and also on earlier DEC programs for
+ 9 the LN01 and LN03 laser printers, Dvi2lng, Topp and LN03Topp. This program
+ 10 is not a DEC product and is not guaranteed to work.
+ 11
+ 12 Dvi2ln3 is written in VAX C. Specific VAX and VMS dependencies have
+ 13 generally been avoided, however. The reader may find them by searching
+ 14 for the strings VAX and VMS in this file.
+ 15
+ 16 During development, double square brackets [[ ]] in a comment indicate some
+ 17 places where the code needs to be improved.
+ 18
+ 19 [[Among the useful things that still need to be done: differentiating
+ 20 between int and long variables so this can be ported more easily to 16-bit
+ 21 architectures; testing to see if malloc returns 0; a \special for ROM
+ 22 fonts; a \special for sixels.]]
+ 23
+ 24 Development history:
+ 25
+ 26 Feb. 85: Did some early work on Dvi2ln3, translating bits and pieces
+ 27 of Dvi2lng. Concluded, however, that it was better to translate
+ 28 LN03Topp and Dvi2lng into C first. The reason for this is that code
+ 29 which mimics an existing program is easier to write and to test.
+ 30
+ 31 5/15/85: At this date, the old LN03Topp program has been successfully
+ 32 translated into C (except for the ability to read files over
+ 33 DECnet). At this point, did some further cleanup of the Dvi2ln3
+ 34 source.
+ 35
+ 36 6/4/85: Coding of Dvi2ln3 begins in earnest. Merging bits and
+ 37 pieces from various places. Chucking chunks and all that
+ 38 junk from LN03Topp; pass2 is just straightforward translation.
+ 39
+ 40 6/22/85: Cleanup after initial debugging. Ready to distribute.
+ 41
+ 42 10/14/85: Bug in parsing of \special's. Version 1.
+ 43
+ 44 */
+ 45
+ 46 #include stdio
+ 104
+ 105 /* To please the VMS linker, we declare all global variables to be
+ 106 globaldef. This is done with #define's, so it may easily be undone when
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 2
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 107 porting the program to other systems. */
+ 108
+ 109 #ifdef vms
+ 110 #define GLOBAL globaldef
+ 111 #define EXTERN globalref
+ 112 #else
+ 113 X #define GLOBAL
+ 114 X #define EXTERN extern
+ 115 #endif
+ 116
+ 117 /* hoff is the horizontal offset in pixels to be added to all dimensions
+ 118 read in; voff is the corresponding vertical offset. */
+ 119
+ 120 GLOBAL int hoff, voff, startp;
+ 121 GLOBAL FILE *dvifile,*tfmfile,*outfile;
+ 122
+ 123 /* Each page in a DVI file is identified by ten longwords, which appear
+ 124 immediately after each bop (beginning of page) command. Dvi2ln3, like
+ 125 DVItype, supports certain options that allow one to print only selected
+ 126 pages. The following variables are used for this purpose.
+ 127
+ 128 Num_pages is a count of how many pages have been passed to the output.
+ 129 Max_pages is the user-specified maximum number of pages to pass.
+ 130 How_many_counts denotes the number of identifying longwords to take into
+ 131 account when searching for the user-specified starting page. The user
+ 132 specifies the starting page as the first one whose identifying longwords
+ 133 match certain specified values, stored in start_page. However, identifying
+ 134 longword i is only required to match the value in start_page[i] if
+ 135 use_count[i] is nonzero. */
+ 136
+ 137 GLOBAL long int num_pages,max_pages;
+ 138 GLOBAL int how_many_counts,use_count[10];
+ 139 GLOBAL long int start_page[10];
+ 140
+ 141 /* FILESPECLEN is the maximum size of a file specification under VMS.
+ 142 [[Perhaps arrays of fixed size should not be used, but rather malloc should
+ 143 be employed...]] */
+ 144
+ 145 #define FILESPECLEN 252
+ 146
+ 147 /* The main routine deals with the command line arguments, opens the dvi
+ 148 file, and then calls various other routines to handle the "passes". There
+ 149 are two passes which involve reading the DVI file; in between, the font
+ 150 load is constructed. */
+ 151
+ 152 main(argc,argv)
+ 153 int argc;
+ 154 char *argv[];
+ 155 {
+ 156 1 int status,i,jnam,jext;
+ 157 1 char infnam[FILESPECLEN];
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 3
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 158 1
+ 159 1 if (argc < 2) { printf("\n Usage: Dvi2ln3 <filename> <options>");
+ 160 2 goto exit_label; }
+ 161 1
+ 162 1 printf("\n Dvi2ln3 1");
+ 163 1
+ 164 1 strcpy(infnam,argv[1]);
+ 165 1
+ 166 1 /* The extension of the input file defaults to .dvi. Locate the filename
+ 167 1 field within the filespec parameter, set jname to point to beginning, jext
+ 168 1 to point to one place after end. [[This code is of course dependent on the
+ 169 1 VMS filespec syntax.]] */
+ 170 1
+ 171 1 find_VMS_filename(infnam,&jnam,&jext);
+ 172 1 if (infnam[jext] == '\0') strcat(infnam,".dvi");
+ 173 1 dvifile = fopen(infnam,"r");
+ 174 1 if (dvifile == NULL) { printf("\n Couldn't open dvi file");
+ 175 2 goto exit_label; }
+ 176 1
+ 177 1 /* Now decipher the options off the command line. */
+ 178 1
+ 179 1 max_pages = 1000000000;
+ 180 1 how_many_counts = 0;
+ 181 1 hoff = 300;
+ 182 1 voff = 300;
+ 183 1 for (i=2; i<argc; i++) command_line_options(argv[i]);
+ 184 1
+ 185 1 status = pass1();
+ 186 1 if (status != 0) goto exit_label;
+ 187 1 if (rewind(dvifile) == -1) {
+ 188 2 printf("\n Couldn't rewind dvi file.");
+ 189 2 goto exit_label;
+ 190 2 }
+ 191 1
+ 192 1 status = open_output_file(infnam,jnam,jext,".ln3");
+ 193 1 if (status != 0) { printf("\n Couldn't open output file");
+ 194 2 goto exit_label; }
+ 195 1
+ 196 1 status = make_font_load();
+ 197 1 if (status != 0) goto exit_label;
+ 198 1
+ 199 1 pass2();
+ 200 1
+ 201 1 fprintf(outfile,"\033c");
+ 202 1 fclose(dvifile);
+ 203 1 fclose(outfile);
+ 204 1
+ 205 1 exit_label:
+ 206 1 ;
+ 207 1 }
+ 208
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 4
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 209 /* Find_VMS_filename finds the filename part of a VMS filespec passed in s,
+ 210 returning the index of the first character in *ns, and the index of the
+ 211 character after the last in *ne.
+ 212
+ 213 [[When porting Dvi2ln3 to a non-VMS system, we need to find an alternative
+ 214 to this function. Under Unix, as a first approximation, we could look for
+ 215 the last "/" or "~" and then for the first succeeding "." in the filespec.
+ 216 Is this correct?]] */
+ 217
+ 218 int find_VMS_filename(s,ns,ne)
+ 219 char s[];
+ 220 int *ns,*ne;
+ 221 {
+ 222 1 int jnam,jext,j,slen;
+ 223 1
+ 224 1 slen = strlen(s);
+ 225 1 jnam = 0;
+ 226 1 for (j = slen-1; j >= 0; j--) {
+ 227 2 if (s[j] == ':' || s[j] == ']' ||
+ 228 2 s[j] == '>') {
+ 229 3 jnam = j+1;
+ 230 3 break;
+ 231 3 }
+ 232 2 }
+ 233 1
+ 234 1 jext = slen;
+ 235 1 for (j = jnam; j < slen; j++) {
+ 236 2 if (s[j] == '.' || s[j] == ';') {
+ 237 3 jext = j;
+ 238 3 break;
+ 239 3 }
+ 240 2 }
+ 241 1
+ 242 1 *ns = jnam;
+ 243 1 *ne = jext;
+ 244 1 }
+ 245
+ 246 /* ERROR HANDLING: Errors should be reported as close to the source as
+ 247 possible, so that the maximum amount of information is available to the
+ 248 user to identify the error.
+ 249
+ 250 Errors in the format of DVI or TFM files are not reported specifically,
+ 251 since there exist programs, DVItype and TFtoPL, which diagnose errors in
+ 252 such files. This program accepts some incorrect DVI files, for example,
+ 253 those with a bad postamble or bad backpointers. [[However, someday we may
+ 254 rewrite the program to select pages by using these features of the DVI
+ 255 format, rather than by skipping unwanted pages.]] */
+ 256
+ 257 GLOBAL char *bad_DVI_message = "\nBad DVI file - check it with DVItype";
+ 258
+ 259 /* Command_line_options reads and processes options off the string s. At
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 5
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 260 this time, four options are supported.
+ 261
+ 262 The N option sets the maximum number of pages to be printed. Its syntax is
+ 263 /N=<integer> .
+ 264
+ 265 The H option modifies the default horizontal offset.
+ 266
+ 267 The V option modifies the default vertical offset.
+ 268
+ 269 The S option has syntax /S=<pagespec>{.<pagespec>}* where the {}* denotes
+ 270 repetition, and a <pagespec> is either an integer or * to indicate any
+ 271 value. An example would be "/S=*.8.3".
+ 272
+ 273 The meaning of the S option is as follows: In a DVI file, pages are
+ 274 identified by ten longword values which follow each bop (beginning of page)
+ 275 command. The value of the S option indicates which page to start printing
+ 276 on. For example, "*.8.3" means start printing at the first page whose
+ 277 second identifying longword is 8 and third is 3. */
+ 278
+ 279 int command_line_options(t)
+ 280 char *t;
+ 281 {
+ 282 1 long int i,k;
+ 283 1 char *u;
+ 284 1
+ 285 1 while ((t = strchr(t,'/')) != 0) {
+ 286 2 t++;
+ 287 2 if (toupper(t[0]) == 'N' && t[1] == '='
+ 288 2 && (sscanf(&t[2],"%ld",&k) == 1))
+ 289 2 max_pages = k;
+ 290 2 if (toupper(t[0]) == 'H' && t[1] == '='
+ 291 2 && (sscanf(&t[2],"%ld",&k) == 1))
+ 292 2 hoff = k;
+ 293 2 if (toupper(t[0]) == 'V' && t[1] == '='
+ 294 2 && (sscanf(&t[2],"%ld",&k) == 1))
+ 295 2 voff = k;
+ 296 2 else if (toupper(t[0]) == 'S' && t[1] == '=') {
+ 297 3 t += 2;
+ 298 3 how_many_counts = 0;
+ 299 3 for (;;) {
+ 300 4 if (t[0] == '*') {
+ 301 5 use_count[how_many_counts] = 0;
+ 302 5 how_many_counts++;
+ 303 5 }
+ 304 4 else if (sscanf(t,"%ld",&k) != 0) {
+ 305 5 use_count[how_many_counts] = 1;
+ 306 5 start_page[how_many_counts] = k;
+ 307 5 how_many_counts++;
+ 308 5 } else break;
+ 309 4 u = strchr(t,'.');
+ 310 4 if (u == 0) break;
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 6
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 311 4 t = &u[1];
+ 312 4 }
+ 313 3 }
+ 314 2 }
+ 315 1 }
+ 316
+ 317 /* Open_output_file opens one of the output files, using the file pointer
+ 318 outfile. The name of the output file is obtained by appending the string
+ 319 ext to the substring of infnam beginning at jnam and ending at jext.
+ 320
+ 321 [[This code contains a VMS depndency. We use creat followed by fdopen to
+ 322 open the file as a normal VMS file ("rat=cr","rfm=var") rather than a
+ 323 STREAM_LF file. Beginning with VAX C V2.0, this can be done by just calling
+ 324 fopen.]] */
+ 325
+ 326 int open_output_file(infnam,jnam,jext,ext)
+ 327 char *infnam,*ext;
+ 328 int jnam,jext;
+ 329 {
+ 330 1 char outfnam[FILESPECLEN];
+ 331 1 int jj;
+ 332 1
+ 333 1 strcpy(outfnam,&infnam[jnam]);
+ 334 1 strcpy(&outfnam[jext-jnam],ext);
+ 335 1
+ 336 1 jj = creat(outfnam,0,"rat=cr","rfm=var");
+ 337 1 if (jj == -1) return(1);
+ 338 1 outfile = fdopen(jj,"w");
+ 339 1 if (outfile == NULL) return(1);
+ 340 1
+ 341 1 return(0);
+ 342 1 }
+ 343
+ 344 /* Bytes are read one by one from the DVI file using getc. We use an
+ 345 overlay, as suggested in the DVItype documentation, to combine these bytes
+ 346 into larger integers. [[Note that this technique relies on the fact that
+ 347 numbers on the VAX are stored with the least significant byte first. The
+ 348 macros below would have to be changed if the program were to be ported to a
+ 349 machine architecture for which this is not so.]]
+ 350
+ 351 [[The use of getc is expensive, since getc is a function in VAX C V2.0.
+ 352 Eventually, one would want to rewrite these macros to work like the old
+ 353 getc macro.]] */
+ 354
+ 355 GLOBAL union lc { long int l; unsigned long int ul; char c[4];
+ 356 unsigned char uc[4]; } lcx;
+ 357
+ 358 #define two_bytes_u lcx.uc[1] = getc(dvifile); \
+ 359 lcx.uc[0] = getc(dvifile); lcx.uc[2] = 0; lcx.uc[3] = 0
+ 360
+ 361 #define two_bytes_s lcx.c[1] = getc(dvifile); \
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 7
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 362 lcx.c[0] = getc(dvifile); \
+ 363 if (lcx.c[1] >= 0) { lcx.uc[2] = 0; lcx.uc[3] = 0 ;} \
+ 364 else { lcx.uc[2] = 255; lcx.uc[3] = 255; }
+ 365
+ 366 #define three_bytes_u lcx.uc[3] = getc(dvifile); lcx.uc[1] = getc(dvifile); \
+ 367 lcx.uc[0] = getc(dvifile); lcx.uc[2] = 0
+ 368
+ 369 #define three_bytes_s lcx.c[2] = getc(dvifile); lcx.c[1] = getc(dvifile); \
+ 370 lcx.c[0] = getc(dvifile); \
+ 371 lcx.uc[3] = (lcx.c[2] >= 0) ? 0 : 255;
+ 372
+ 373 #define four_bytes lcx.c[3] = getc(dvifile); \
+ 374 lcx.c[2] = getc(dvifile); lcx.c[1] = getc(dvifile); \
+ 375 lcx.c[0] = getc(dvifile);
+ 376
+ 377 /* Knuth's programs like to hardcode fixed maximum sizes for various
+ 378 things, for example, the maximum number of fonts allowed in a DVI file. In
+ 379 general, it is preferable to use the C function malloc to allocate storage
+ 380 as needed. That is what we generally do in this Dvi2ln3, but there are some
+ 381 residues of the Knuthian approach, like MAXTEXFONTS below. By the way, we
+ 382 never attempt to return any storage to the system.
+ 383
+ 384 [[Eventually, it would be better to get rid of MAXTEXFONTS and use a linked
+ 385 list of records instead. There would be no cpu time penalty to using a
+ 386 linked list, because the function set_curf below does a linear search
+ 387 through the font array anyway.]] */
+ 388
+ 389 #define MAXTEXFONTS 100
+ 390
+ 391 struct txf { unsigned char chu[256]; int bc, ec; long int space, design_size,
+ 392 scaled_size; int nchs; };
+ 393
+ 394 GLOBAL struct txf *txfa[MAXTEXFONTS+1];
+ 395
+ 396 /* Font width information needs to be read from TFM files. TFM is a special
+ 397 format defined by TeX. Dvi2ln3 stores each width in a longword. DVItype
+ 398 tries to save space by a two-level width storage method. We just allocate
+ 399 an array of widths for each font with malloc.
+ 400
+ 401 [[We don't check that font checksums match in Dvi2ln3, because there are a
+ 402 lot of slightly obsolete TFMs floating around which would result in a
+ 403 checksum error, but seem to give perfectly reasonable formatted output
+ 404 nonetheless.]] */
+ 405
+ 406 GLOBAL long int *font_width[MAXTEXFONTS+1];
+ 407
+ 408 /* TeX fonts are referred to by their internal numbers, which go from 0 to
+ 409 nf-1. The DVI file refers to them by external numbers, hence the array
+ 410 to_ext used to convert internal numbers to external numbers. Curf is the
+ 411 internal font number of the current font in the DVI file. */
+ 412
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 8
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 413 GLOBAL int to_ext[MAXTEXFONTS+1],
+ 414 nf,
+ 415 curf;
+ 416
+ 417 /* In some switch statements, a lot of cases have to be enumerated. We
+ 418 employ this Knuthian macro for that purpose: */
+ 419
+ 420 #define four_cases(_x1) case _x1: case _x1+1: case _x1+2: case _x1+3:
+ 421
+ 422 /* We define a lot of constants corresponding to the DVI operation codes.
+ 423 These are copied from DVItype. */
+ 424
+ 425 #define id_byte 2 /* current version of the dvi format */
+ 426 #define set_char_0 0 /* typeset character 0 and move right */
+ 427 #define set1 128 /* typeset a character and move right */
+ 428 #define set_rule 132 /* typeset a rule and move right */
+ 429 #define put1 133 /* typeset a character */
+ 430 #define put_rule 137 /* typeset a rule */
+ 431 #define nop 138 /* no operation */
+ 432 #define bop 139 /* beginning of page */
+ 433 #define eop 140 /* ending of page */
+ 434 #define push 141 /* save the current positions */
+ 435 #define pop 142 /* restore previous positions */
+ 436 #define right1 143 /* move right */
+ 437 #define w0 147 /* move right by |w| */
+ 438 #define w1 148 /* move right and set |w| */
+ 439 #define x0 152 /* move right by |x| */
+ 440 #define x1 153 /* move right and set |x| */
+ 441 #define down1 157 /* move down */
+ 442 #define y0 161 /* move down by |y| */
+ 443 #define y1 162 /* move down and set |y| */
+ 444 #define z0 166 /* move down by |z| */
+ 445 #define z1 167 /* move down and set |z| */
+ 446 #define fnt_num_0 171 /* set current font to 0 */
+ 447 #define fnt1 235 /* set current font */
+ 448 #define xxx1 239 /* extension to dvi primitives (\special) */
+ 449 #define xxx4 242 /* potentially long extension to dvi primitives */
+ 450 #define fnt_def1 243 /* define the meaning of a font number */
+ 451 #define pre 247 /* preamble */
+ 452 #define post 248 /* postamble beginning */
+ 453 #define post_post 249 /* postamble ending */
+ 454 #define undefined_command 250
+ 455
+ 456 GLOBAL long int mag, num, den;
+ 457 GLOBAL float conv, unmag_conv;
+ 458
+ 459 /* Read_preamble reads the preamble of the dvi file. The comment is thrown
+ 460 away. The format version number is checked. The magnification, numerator,
+ 461 and denominator are remembered in the globals mag, num, den. The float
+ 462 values conv and unmag_conv serve to convert measurements from DVI units to
+ 463 pixels. */
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 9
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 464
+ 465 int read_preamble() {
+ 466 1 unsigned int i;
+ 467 1 int j;
+ 468 1
+ 469 1 i = getc(dvifile);
+ 470 1 if (i != pre) return(1);
+ 471 1 i = getc(dvifile);
+ 472 1 if (i != id_byte) return(1);
+ 473 1 four_bytes; num = lcx.l;
+ 474 1 if (num <= 0) return(1);
+ 475 1 four_bytes; den = lcx.l;
+ 476 1 if (den <= 0) return(1);
+ 477 1 four_bytes; mag = lcx.l;
+ 478 1 if (mag <= 0) return(1);
+ 479 1
+ 480 1 unmag_conv = (num/254000.0) * (300.0/den);
+ 481 1 conv = unmag_conv * (mag/1000.0);
+ 482 1
+ 483 1 /* Skip over the comment field. */
+ 484 1
+ 485 1 i = getc(dvifile);
+ 486 1 for (j=0; j<i; j++) getc(dvifile);
+ 487 1 return(0);
+ 488 1 }
+ 489
+ 490 /* The dvi file format requires us to keep track of various quantities,
+ 491 among them the horizontal and vertical positions h and v, and four values
+ 492 referred to simply as x, y, z and w. We also keep pixel-rounded versions
+ 493 of h and v, called hh and vv. */
+ 494
+ 495 GLOBAL long h, v, x, y, z, w;
+ 496 GLOBAL int hh, vv;
+ 497
+ 498 /* The following function, copied from DVItype, reads from the DVI file the
+ 499 first parameter of the current DVI command. In the case of functions whose
+ 500 first parameter is implicit, such as set_char_0 through set_char_127, the
+ 501 implicit parameter is returned. */
+ 502
+ 503 long first_par(o)
+ 504 int o;
+ 505 {
+ 506 1 unsigned int i;
+ 507 1
+ 508 1 if ((o >= set_char_0) && (o < set_char_0+128))
+ 509 1 return(o-set_char_0);
+ 510 1 if ((o >= fnt_num_0) && (o < fnt_num_0+64))
+ 511 1 return(o-fnt_num_0);
+ 512 1 switch (o) {
+ 513 2 case set1:
+ 514 2 case put1:
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 10
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 515 2 case fnt1:
+ 516 2 case xxx1:
+ 517 2 case fnt_def1:
+ 518 2 i = getc(dvifile);
+ 519 2 return(i);
+ 520 2 case set1+1:
+ 521 2 case put1+1:
+ 522 2 case fnt1+1:
+ 523 2 case xxx1+1:
+ 524 2 case fnt_def1+1:
+ 525 2 two_bytes_u;
+ 526 2 return(lcx.ul);
+ 527 2 case set1+2:
+ 528 2 case put1+2:
+ 529 2 case fnt1+2:
+ 530 2 case xxx1+2:
+ 531 2 case fnt_def1+2:
+ 532 2 three_bytes_u;
+ 533 2 return(lcx.ul);
+ 534 2 case right1:
+ 535 2 case w1:
+ 536 2 case x1:
+ 537 2 case down1:
+ 538 2 case y1:
+ 539 2 case z1:
+ 540 2 return(getc(dvifile));
+ 541 2 case right1+1:
+ 542 2 case w1+1:
+ 543 2 case x1+1:
+ 544 2 case down1+1:
+ 545 2 case y1+1:
+ 546 2 case z1+1:
+ 547 2 two_bytes_s;
+ 548 2 return(lcx.l);
+ 549 2 case right1+2:
+ 550 2 case w1+2:
+ 551 2 case x1+2:
+ 552 2 case down1+2:
+ 553 2 case y1+2:
+ 554 2 case z1+2:
+ 555 2 three_bytes_s;
+ 556 2 return(lcx.l);
+ 557 2 case set_rule:
+ 558 2 case put_rule:
+ 559 2 case right1+3:
+ 560 2 case w1+3:
+ 561 2 case x1+3:
+ 562 2 case down1+3:
+ 563 2 case y1+3:
+ 564 2 case z1+3:
+ 565 2 case set1+3:
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 11
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 566 2 case put1+3:
+ 567 2 case fnt1+3:
+ 568 2 case xxx1+3:
+ 569 2 case fnt_def1+3:
+ 570 2 four_bytes;
+ 571 2 return(lcx.l);
+ 572 2 case w0:
+ 573 2 return(w);
+ 574 2 case x0:
+ 575 2 return(x);
+ 576 2 case y0:
+ 577 2 return(y);
+ 578 2 case z0:
+ 579 2 return(z);
+ 580 2 default:
+ 581 2 return(0);
+ 582 2 }
+ 583 1 }
+ 584
+ 585 /* Pass1 reads from the DVI file until the starting page condition is met.
+ 586 Return 1 if something goes wrong, or if the DVI file comes to an end. This
+ 587 resembles skip_pages below. */
+ 588
+ 589 int pass1() {
+ 590 1
+ 591 1 unsigned int k;
+ 592 1 long int p;
+ 593 1 int i;
+ 594 1 char startp;
+ 595 1
+ 596 1 if (read_preamble() != 0) {
+ 597 2 printf(bad_DVI_message);
+ 598 2 return(1);
+ 599 2 }
+ 600 1
+ 601 1 for (;;) {
+ 602 2 if (feof(dvifile)) {
+ 603 3 printf(bad_DVI_message);
+ 604 3 return(1);
+ 605 3 }
+ 606 2 k = getc(dvifile);
+ 607 2 if (k == post) return(0);
+ 608 2 p = first_par(k);
+ 609 2 if (k >= set_char_0 && k < set_char_0+128) k = set1;
+ 610 2 if (k >= fnt_num_0 && k < fnt_num_0+64) k = fnt1;
+ 611 2
+ 612 2 switch (k) {
+ 613 3 case bop:
+ 614 3 startp = 1;
+ 615 3 for (i=0; i<10; i++) {
+ 616 4 four_bytes;
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 12
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 617 4 if (i < how_many_counts && use_count[i] &&
+ 618 4 lcx.l != start_page[i]) startp = 0;
+ 619 4 }
+ 620 3 four_bytes;
+ 621 3 if (startp != 0) num_pages = 1;
+ 622 3 else if (num_pages > 0) {
+ 623 4 num_pages++;
+ 624 4 if (num_pages > max_pages) return(0);
+ 625 4 }
+ 626 3 break;
+ 627 3 case set_rule:
+ 628 3 case put_rule:
+ 629 3 four_bytes;
+ 630 3 break;
+ 631 3 four_cases(fnt_def1)
+ 632 3 i = define_font(p);
+ 633 3 if (i != 0) return(1);
+ 634 3 break;
+ 635 3 four_cases(fnt1)
+ 636 3 if (num_pages > 0) set_curf(p);
+ 637 3 break;
+ 638 3 four_cases(set1)
+ 639 3 four_cases(put1)
+ 640 3 if (num_pages > 0)
+ 641 3 txfa[curf] -> chu[p] = 1;
+ 642 3 break;
+ 643 3 four_cases(xxx1)
+ 644 3 for (; p>0; p--) getc(dvifile);
+ 645 3 break;
+ 646 3 default:
+ 647 3 break;
+ 648 3 }
+ 649 2 }
+ 650 1 return(0);
+ 651 1 }
+ 652
+ 653 #define leftfirst 33
+ 654 #define rightfirst 161
+ 655 #define leftlast 126
+ 656 #define rightlast 254
+ 657
+ 658 /* The define of maxnfonts below is based on a belief, perhaps
+ 659 superstitious, that there is a limit of 31 downline-loaded fonts in the
+ 660 LN03. For us an nfont is two LN03 fonts, hence the following definition. */
+ 661
+ 662 #define maxnfonts 16
+ 663
+ 664 /* Txf is a record structure describing a TeX font. Txfa is an array of txf
+ 665 records. Txf2lnf describes the mapping between TeX fonts and pairs of LN03
+ 666 fonts. Because the constructed LN03 font load has just the glyphs that are
+ 667 needed, we can often cram more than one TeX font into a pair of LN03 fonts.
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 13
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 668
+ 669 Why a pair of fonts? Because LN03 fonts are allowed to hold 94 glyphs only.
+ 670 They come in two flavors, "left" which answer to character codes 33 to 126,
+ 671 and "right" which answer to codes 161 to 254. LN03Topp assigns each TeX
+ 672 font to one "lnfno", where each lnfno denotes a pair of LN03 fonts, one
+ 673 left, one right. */
+ 674
+ 675 GLOBAL int txf2lnf[MAXTEXFONTS];
+ 676
+ 677 /* Maxfontnos reflects the fact that all LN03 fonts must be denoted by
+ 678 a number from 10 to 19. */
+ 679
+ 680 #define maxfontnos 9
+ 681
+ 682 GLOBAL int lastch[maxnfonts];
+ 683 GLOBAL unsigned char chw[maxnfonts][256];
+ 684 GLOBAL char fname[maxnfonts][32];
+ 685
+ 686 /* The LN03 font-denoting numbers 10-19 have to be allocated among the
+ 687 fonts. The useno array keeps track of which number a font is allowed to
+ 688 use, -1 if none is currently allocated. The whouses array says which font
+ 689 is using a number. */
+ 690
+ 691 GLOBAL int useno[maxnfonts],whouses[maxfontnos];
+ 692
+ 693 /* Font_name points to strings containing font names, which are created
+ 694 with malloc as needed. */
+ 695
+ 696 GLOBAL char *font_name[MAXTEXFONTS+1];
+ 697
+ 698 GLOBAL int maxrmar,maxbmar;
+ 699
+ 700 #define max(x,y) (((x)>(y))?(x):(y))
+ 701 #define min(x,y) (((x)<(y))?(x):(y))
+ 702
+ 703 /* Make_font_load calls on other procedures to generate the LN03 font load.
+ 704 */
+ 705
+ 706 int make_font_load() {
+ 707 1
+ 708 1 int i,j,jj,k,l,fno,maxfno,chsize;
+ 709 1 long int totsize;
+ 710 1 int lnfcnt,txfcnt,the_txf,txf_size,lnfleft;
+ 711 1 int txford[MAXTEXFONTS];
+ 712 1 char cnt[3];
+ 713 1
+ 714 1 totsize = 0;
+ 715 1 chsize = 0;
+ 716 1 curf = 0;
+ 717 1 for (i=0; i<maxnfonts; i++)
+ 718 1 for (j=0; j<256; j++) chw[i][j] = 0;
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 14
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 719 1
+ 720 1 for (jj=0; jj<MAXTEXFONTS; jj++) txf2lnf[jj] = -1;
+ 721 1
+ 722 1 /* Fill the fname strings with different valid LN03 font names. */
+ 723 1
+ 724 1 for (i=0; i<maxnfonts; i++)
+ 725 1 strcpy(fname[i],"U000000002SK00GG0001UZZZZ02F000");
+ 726 1 for (jj=1; jj<maxnfonts; jj++) {
+ 727 2 sprintf(cnt,"%02d",jj);
+ 728 2 strncpy(&(fname[jj][5]),cnt,2);
+ 729 2 }
+ 730 1
+ 731 1 /* The margins are set to values appropriate for American 8 1/2 by 11
+ 732 1 paper. It is not clear if this needs to be changed for European A4 paper.
+ 733 1 Not changing it might deprive Europeans of access to the bottom 1.5cm of
+ 734 1 their paper. */
+ 735 1
+ 736 1 maxrmar = 2550;
+ 737 1 maxbmar = 3300;
+ 738 1
+ 739 1 /* In the following, esc[?27h means "advance the carriage by the character
+ 740 1 width when you set a character", esc[11h and esc[7 I together mean to
+ 741 1 interpret all dimensions in escape sequences as pixel units; esc[?52h means
+ 742 1 our origin of coordinates is the upper left edge of the paper; esc[%dt
+ 743 1 means the "maximum length" of the paper is maxbmar pixels. */
+ 744 1
+ 745 1 fprintf(outfile,"\033c\033[?27h\033[11h\033[7 I\033[?52h\033[%dt\n",
+ 746 1 maxbmar);
+ 747 1
+ 748 1 for (i=0; i<maxnfonts; i++) useno[i] = -1;
+ 749 1 for (i=0; i<maxfontnos; i++) whouses[i] = -1;
+ 750 1 maxfno = -1;
+ 751 1
+ 752 1 /* Write font loading escape sequence onto outf. [[It may be desirable to
+ 753 1 make this escape sequence clear all dowloaded fonts from RAM.]] */
+ 754 1
+ 755 1 fprintf(outfile,"\033P0;1;1y");
+ 756 1
+ 757 1 /* Count how many glyphs each font requires. */
+ 758 1
+ 759 1 for (fno=0; fno<nf; fno++) {
+ 760 2
+ 761 2 txfa[fno] -> nchs = 0;
+ 762 2 for(i=0; i<256; i++)
+ 763 2 if (txfa[fno] -> chu[i] != 0) txfa[fno] -> nchs++;
+ 764 2
+ 765 2 if (txfa[fno] -> nchs > 188) goto need_empty_slots;
+ 766 2 chsize += txfa[fno] -> nchs;
+ 767 2 }
+ 768 1 printf("\nFont load to contain %d glyphs.",chsize);
+ 769 1
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 15
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 770 1
+ 771 1 /* Now we have to allocate TeX fonts to LN03 fonts pairs and perform the
+ 772 1 actual load. The goal is to use as few slots as possible, where an LN03
+ 773 1 font pair with <= 94 glyphs is one slot, and a font pair with > 94 but <=
+ 774 1 188 glyphs is two slots.
+ 775 1
+ 776 1 As in the LN01 case, the allocation problem seems to be some sort of hard
+ 777 1 bin packing problem. (Proof of NP-hardness, anyone?) So we use a best fit
+ 778 1 heuristic:
+ 779 1
+ 780 1 1. Find the unassigned largest TeX font. If none, exit, we're done.
+ 781 1
+ 782 1 2. Allocate an LN03 font pair for that TeX font and put the TeX
+ 783 1 font into it.
+ 784 1
+ 785 1 3. Find the largest remaining TeX font that fits in what is left of
+ 786 1 that LN03 font pair.
+ 787 1
+ 788 1 3.1. if none exists, go to 1.
+ 789 1 3.2. if one exists, put it in the font pair and go back to 3.
+ 790 1
+ 791 1 The txford array contains the TeX font numbers in the order that they are
+ 792 1 assigned. Txfcnt keeps track of how many TeX fonts have been assigned so
+ 793 1 far. */
+ 794 1
+ 795 1 lnfcnt = 0;
+ 796 1 txfcnt = 0;
+ 797 1 for (i=0; i<MAXTEXFONTS; i++) txford[i] = -1;
+ 798 1
+ 799 1 while (1) {
+ 800 2
+ 801 2 /* Find largest unassigned TeX font */
+ 802 2
+ 803 2 lnfleft = rightlast-rightfirst+1+leftlast-leftfirst+1;
+ 804 2 txf_size = -1;
+ 805 2 for (j = 0; j < MAXTEXFONTS; j++) {
+ 806 3 if (txfa[j] != 0 && txf2lnf[j] == -1
+ 807 3 && txfa[j] -> nchs > txf_size) {
+ 808 4 txf_size = txfa[j] -> nchs;
+ 809 4 the_txf = j;
+ 810 4 }
+ 811 3 }
+ 812 2 if (txf_size <= 0) goto assignment_done;
+ 813 2 if (lnfcnt > maxnfonts) goto too_complex;
+ 814 2 if (txf_size > lnfleft) goto need_empty_slots;
+ 815 2
+ 816 2 /* Now allocate new LN03 font pair and put the current TeX font into it */
+ 817 2
+ 818 2 txf2lnf[the_txf] = lnfcnt;
+ 819 2 txford[txfcnt] = the_txf;
+ 820 2 txfcnt++;
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 16
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 821 2 k = leftfirst-1;
+ 822 2 for (j = 0; j <= 255; j++) {
+ 823 3 if (txfa[the_txf] -> chu[j] != 0) {
+ 824 4 k++;
+ 825 4 if (k == leftlast+1) k = rightfirst;
+ 826 4 lnfleft--;
+ 827 4 txfa[the_txf] -> chu[j] = k;
+ 828 4 }
+ 829 3 }
+ 830 2 lastch[lnfcnt] = k;
+ 831 2
+ 832 2 /* Now try to fill the remaining part of the LN03 font pair using other TeX
+ 833 2 fonts */
+ 834 2
+ 835 2 while (1) {
+ 836 3
+ 837 3 txf_size = -1;
+ 838 3 for (j = 0; j < MAXTEXFONTS; j++) {
+ 839 4 if (txfa[j] != 0 && txf2lnf[j] == -1
+ 840 4 && txfa[j] -> nchs > txf_size
+ 841 4 && txfa[j] -> nchs <= lnfleft) {
+ 842 5 txf_size = txfa[j] -> nchs;
+ 843 5 the_txf = j;
+ 844 5 }
+ 845 4 }
+ 846 3 if (txf_size <= 0) break;
+ 847 3
+ 848 3 txf2lnf[the_txf] = lnfcnt;
+ 849 3 txford[txfcnt] = the_txf;
+ 850 3 txfcnt++;
+ 851 3 k = lastch[lnfcnt];
+ 852 3 for (j=0; j<=255; j++) {
+ 853 4 if (txfa[the_txf] -> chu[j] != 0) {
+ 854 5 k++;
+ 855 5 lnfleft--;
+ 856 5 if (k == leftlast+1) k = rightfirst;
+ 857 5 txfa[the_txf] -> chu[j] = k;
+ 858 5 }
+ 859 4 }
+ 860 3 lastch[lnfcnt] = k;
+ 861 3 }
+ 862 2
+ 863 2 lnfcnt++;
+ 864 2 }
+ 865 1
+ 866 1 assignment_done:
+ 867 1
+ 868 1 /* At this point the array txf2lnf is properly initialized, as are lastch
+ 869 1 and txford. It remains to actually generate the desired font load. This has
+ 870 1 to be done carefully, since the function add_txf_to_lnf only supports
+ 871 1 adding glyphs to an LN03 font pair in ascending order of character code. */
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 17
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 872 1
+ 873 1 for (j=0; j<txfcnt; j++) {
+ 874 2 k = txford[j];
+ 875 2 if (j != 0 && txf2lnf[k] != txf2lnf[txford[j-1]])
+ 876 2 fprintf(outfile,",\n");
+ 877 2 totsize += add_txf_to_lnf(txf2lnf[k],k);
+ 878 2 }
+ 879 1
+ 880 1 /* [[At this point we ought to add code that writes a message saying how
+ 881 1 much font RAM the load will occupy. But there is no documentation for
+ 882 1 determining that! So, we make the following compromise:]] */
+ 883 1
+ 884 1 printf("\n Approximate size of font load: %ld bytes",totsize);
+ 885 1
+ 886 1 /* [[We may want to insert a few \n's in the following to keep the line
+ 887 1 from getting too long.]] */
+ 888 1
+ 889 1 fprintf(outfile,"\n;Dvi2ln3 1 font load\033\\");
+ 890 1 fprintf(outfile,"\033[1;%ds\033[%dt\033[1;%dr",maxrmar,maxbmar,maxbmar);
+ 891 1 for (j=0; j <= min(maxfontnos,lnfcnt-1); j++) {
+ 892 2 useno[j] = j;
+ 893 2 whouses[j] = j;
+ 894 2 k = fname[j][16];
+ 895 2 fname[j][16] = '\0';
+ 896 2 fprintf(outfile,"\033P1;1%d}%s\033\\",j,fname[j]);
+ 897 2 fname[j][16] = k;
+ 898 2 }
+ 899 1 fprintf(outfile,"\033[10m");
+ 900 1
+ 901 1 return(0);
+ 902 1
+ 903 1 too_complex:
+ 904 1 printf("\n Can't construct a font load:");
+ 905 1 printf("\n Dvi file uses too many glyphs from too many different fonts.");
+ 906 1 return(1);
+ 907 1
+ 908 1 /* [[We need to determine what the real limit is on the number of LN03
+ 909 1 fonts, and refine the test to detect if it is being exceeded. Fortunately,
+ 910 1 only a very complex DVI file could exceed the limit.]] */
+ 911 1
+ 912 1 need_empty_slots:
+ 913 1 printf("\nFont %s uses > 188 characters. Can't handle that.",
+ 914 1 font_name[fno]);
+ 915 1 return(1);
+ 916 1
+ 917 1 /* [[The error message above should be fixed to specify the magnification
+ 918 1 of the font also.]] */
+ 919 1
+ 920 1 }
+ 921
+ 922 /* The lines in the LN3 file are limited to lengths of about 100 bytes. We
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 18
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 923 keep track of how many bytes are written so far in the global lnhp. The
+ 924 accounting is conservative, so e.g. each pixel position counts as 4 bytes
+ 925 even though it might be shorter. [[One could easily increase 100 to 200. It
+ 926 is not clear what problems are provoked by going over, say, 256.]]
+ 927
+ 928 Vpset keeps track of whether the vertical position needs to be output to
+ 929 the LN03 file before setting any more characters. Hh_old keeps track of the
+ 930 horizontal position which LN03 thinks it's at. */
+ 931
+ 932 GLOBAL int ln3p,vpset,hh_old;
+ 933
+ 934 #define inc_ln3p(x) ln3p += x; if (ln3p > 100) \
+ 935 { ln3p = 0; vpset = 0; hh_old = 30000; }
+ 936
+ 937 /* [[In the above macro, hh_old is set to 30000 to mean "a very large
+ 938 number" (the maximum valid hh for an LN03 is 2550), so as to force the code
+ 939 below to re-output the true hh. What are the implications of this kludge?
+ 940 Would it not be more reasonable to have instead an hhset variable?]] */
+ 941
+ 942 /* A stack of hvxyzw values is kept; the stack pointer is called s.
+ 943 [[Again, it would be better to make this stack a linked list.]] */
+ 944
+ 945 #define STACKSIZE 100
+ 946
+ 947 GLOBAL long hstack[STACKSIZE], vstack[STACKSIZE], xstack[STACKSIZE],
+ 948 ystack[STACKSIZE], zstack[STACKSIZE], wstack[STACKSIZE];
+ 949 GLOBAL int hhstack[STACKSIZE], vvstack[STACKSIZE];
+ 950 GLOBAL int s;
+ 951
+ 952 /* DVI files specify distances in units of 2^-16 points. When translating
+ 953 to a device-specific format, it is necessary to round the DVI distances to
+ 954 pixel units. This is done by means of the pixel_round macro.
+ 955
+ 956 However, rather than using this macro in the straightforward way, rounding
+ 957 is often performed by more elaborate techniques, which we call "Stanford
+ 958 rules" (after John Le Carre's "Moscow rules"). These rules make use of a
+ 959 parameter MAX_DRIFT, which is roughly the maximum number of pixels that
+ 960 things are allowed to deviate from straightforward rounding. */
+ 961
+ 962 #define pixel_round(x) ((int) ((x<0) ? \
+ 963 (conv*(x)-0.5) : (conv*(x)+0.5)))
+ 964
+ 965 #define MAX_DRIFT 2
+ 966
+ 967 /* Pass2 reads the dvi file and interprets the commands in it, usually by
+ 968 calling other routines. The interpretation generally consists of writing
+ 969 something into the ln3 file, updating the current fonts and positions, and
+ 970 possibly updating the stack. */
+ 971
+ 972 int pass2()
+ 973 {
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 19
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 974 1 int i,j;
+ 975 1 long int p;
+ 976 1 unsigned int k;
+ 977 1
+ 978 1 if (read_preamble() != 0) {
+ 979 2 printf(bad_DVI_message);
+ 980 2 return(1);
+ 981 2 }
+ 982 1
+ 983 1 curf = 0;
+ 984 1 ln3p = 0;
+ 985 1 s = 0;
+ 986 1 vpset = 0;
+ 987 1 hh_old = 30000;
+ 988 1
+ 989 1 /* Skip pages until the desired starting page is reached. A nonzero value
+ 990 1 is returned if the starting page is never encountered. */
+ 991 1
+ 992 1 if (skip_pages() != 0) return(0);
+ 993 1
+ 994 1 for (;;) {
+ 995 2 if feof(dvifile) {
+ 996 3 printf(bad_DVI_message);
+ 997 3 return(1); }
+ 998 2 k = getc(dvifile);
+ 999 2 if (k == post) return(0);
+ 1000 2 if (k >= undefined_command) {
+ 1001 3 printf(bad_DVI_message);
+ 1002 3 return(1); }
+ 1003 2
+ 1004 2 p = first_par(k);
+ 1005 2
+ 1006 2 /* If the opcode is an "implicit parameter" opcode, either set_char_n and
+ 1007 2 fnt_num_n, we change it to an explicit parameter opcode to make the case
+ 1008 2 jumps more reasonable. */
+ 1009 2
+ 1010 2 if (k >= set_char_0 && k < set_char_0+128) k = set1;
+ 1011 2 if (k >= fnt_num_0 && k < fnt_num_0+64) k = fnt1;
+ 1012 2
+ 1013 2 j = do_command(k,p);
+ 1014 2 if (j == 2) return(0); /* done with the required number of pages */
+ 1015 2 else if (j == 1) return(1); /* error encountered, stop */
+ 1016 2
+ 1017 2 }
+ 1018 1 }
+ 1019
+ 1020 /* Read from the DVI file until the starting page condition is met. Return
+ 1021 1 if something goes wrong, or if the DVI file comes to an end. This
+ 1022 function is copied quite closely from DVItype. */
+ 1023
+ 1024 int skip_pages() {
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 20
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1025 1
+ 1026 1 unsigned int k;
+ 1027 1 int i;
+ 1028 1 long p,first_page;
+ 1029 1 char startp;
+ 1030 1
+ 1031 1 for (;;) {
+ 1032 2 if (feof(dvifile)) return(1);
+ 1033 2 k = getc(dvifile);
+ 1034 2 if (k == post) return(1);
+ 1035 2 p = first_par(k);
+ 1036 2 if (k >= set_char_0 && k < set_char_0+128) k = set1;
+ 1037 2 if (k >= fnt_num_0 && k < fnt_num_0+64) k = fnt1;
+ 1038 2
+ 1039 2 switch (k) {
+ 1040 3 case bop:
+ 1041 3 startp = 1;
+ 1042 3 for (i=0; i<10; i++) {
+ 1043 4 four_bytes;
+ 1044 4 if (i == 0) first_page = lcx.l;
+ 1045 4 if (i < how_many_counts && use_count[i] &&
+ 1046 4 lcx.l != start_page[i]) startp = 0;
+ 1047 4 }
+ 1048 3 four_bytes;
+ 1049 3 if (startp != 0) {
+ 1050 4 v = 0; vv = 0; h = 0; hh = 0;
+ 1051 4 printf("\n [%ld]",first_page);
+ 1052 4 num_pages = 1; return(0); }
+ 1053 3 break;
+ 1054 3 case set_rule:
+ 1055 3 case put_rule:
+ 1056 3 four_bytes;
+ 1057 3 break;
+ 1058 3 four_cases(fnt_def1)
+ 1059 3 i = define_font(p);
+ 1060 3 if (i != 0) return(1);
+ 1061 3 break;
+ 1062 3 four_cases(xxx1)
+ 1063 3 for (; p>0; p--) getc(dvifile);
+ 1064 3 break;
+ 1065 3 default:
+ 1066 3 break;
+ 1067 3 }
+ 1068 2 }
+ 1069 1 return(0);
+ 1070 1 }
+ 1071
+ 1072 /* Do_command performs the DVI command of code k, assuming the "first
+ 1073 parameter" is p. It is assumed that k is not one of set_char0 through
+ 1074 set_char127. */
+ 1075
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 21
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1076 int do_command(k,p)
+ 1077 int k;
+ 1078 long p;
+ 1079 {
+ 1080 1 int i,j,l,lnf;
+ 1081 1
+ 1082 1 switch (k) {
+ 1083 2 four_cases(fnt_def1)
+ 1084 2 define_font_pass2();
+ 1085 2 break;
+ 1086 2
+ 1087 2 /* [[In processing the put and set DVI commands, no check is made that the
+ 1088 2 character being set actually exists in the font. This can lead to messy
+ 1089 2 errors, like access violations or garbage values when one tries to find
+ 1090 2 that character's width. Neither is any check made that the horizontal and
+ 1091 2 vertical positions fall within the page.]] */
+ 1092 2
+ 1093 2 four_cases(put1)
+ 1094 2 four_cases(set1)
+ 1095 2 if (!vpset) {
+ 1096 3 fprintf(outfile,"\n\033[%dd\033[%d`",vv+voff,hh+hoff);
+ 1097 3 ln3p = 16;
+ 1098 3 vpset = 1;
+ 1099 3 hh_old = hh;
+ 1100 3 }
+ 1101 2 if (hh_old != hh) {
+ 1102 3 if (hh > hh_old) fprintf(outfile,"\033[%da",hh-hh_old);
+ 1103 3 else fprintf(outfile,"\033[%d`",hh+hoff);
+ 1104 3 ln3p += 7;
+ 1105 3 }
+ 1106 2 putc(txfa[curf] -> chu[p],outfile);
+ 1107 2 inc_ln3p(1);
+ 1108 2 if (k >= put1) {
+ 1109 3 hh_old = hh;
+ 1110 3 break;
+ 1111 3 }
+ 1112 2 h += font_width[curf][p];
+ 1113 2
+ 1114 2 /* In rounding h to generate the pixel-position hh, Stanford rules (see
+ 1115 2 above) come into play. We set the new hh (horizontal position in pixels) to
+ 1116 2 the value obtained by adding the pixel width of the character being set to
+ 1117 2 the current position. We then correct this value so that it does not exceed
+ 1118 2 the rounded version of the true position by more than MAX_DRIFT pixels.
+ 1119 2
+ 1120 2 Note that if we did not apply Stanford rules here, or equivalently if we
+ 1121 2 set MAX_DRIFT to zero, many more set-X-position commands would appear in
+ 1122 2 the output. */
+ 1123 2
+ 1124 2 hh += chw[txf2lnf[curf]][txfa[curf] -> chu[p]];
+ 1125 2 hh_old = hh;
+ 1126 2 l = pixel_round(h);
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 22
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1127 2 if (hh-l > MAX_DRIFT) hh = l+MAX_DRIFT;
+ 1128 2 else if (l-hh > MAX_DRIFT) hh = l-MAX_DRIFT;
+ 1129 2 break;
+ 1130 2
+ 1131 2 four_cases(fnt1)
+ 1132 2 set_curf(p);
+ 1133 2 lnf = txf2lnf[curf];
+ 1134 2 if (useno[lnf] == -1) {
+ 1135 3 useno[whouses[maxfontnos]] = -1;
+ 1136 3 useno[lnf] = maxfontnos;
+ 1137 3 whouses[maxfontnos] = lnf;
+ 1138 3 fprintf(outfile,"\033P1;1%d}%16s\033\\",maxfontnos,
+ 1139 3 fname[lnf]);
+ 1140 3 inc_ln3p(26);
+ 1141 3 }
+ 1142 2 fprintf(outfile,"\033[1%dm",useno[lnf]);
+ 1143 2 inc_ln3p(5);
+ 1144 2 break;
+ 1145 2
+ 1146 2 case set_rule:
+ 1147 2 case put_rule:
+ 1148 2
+ 1149 2 /* When converting rule dimensions to pixel dimensions, we do not follow
+ 1150 2 Stanford rules. Rather, we just round the true positions to obtain the
+ 1151 2 pixel positions. This avoids unsightly gaps between rules. [[It should not
+ 1152 2 cause much of a problem with typical rule applications (ruled tables,
+ 1153 2 fraction bars)...but perhaps with large delimiters there might be some
+ 1154 2 difficulty. This needs more thought...]] */
+ 1155 2
+ 1156 2 four_bytes;
+ 1157 2 if (p >= 0 && lcx.l >= 0)
+ 1158 2 do_rule(pixel_round(h),
+ 1159 2 pixel_round(v-p),pixel_round(h+lcx.l),
+ 1160 2 pixel_round(v));
+ 1161 2 if (k == set_rule) {
+ 1162 3 h += lcx.l;
+ 1163 3 hh = pixel_round(h);
+ 1164 3 }
+ 1165 2 break;
+ 1166 2
+ 1167 2 case push:
+ 1168 2 s++;
+ 1169 2 if (s == STACKSIZE) {
+ 1170 3 printf("\n Stack too deep for Dvi2ln3");
+ 1171 3 return(1);
+ 1172 3 }
+ 1173 2 xstack[s] = x;
+ 1174 2 ystack[s] = y;
+ 1175 2 vstack[s] = v;
+ 1176 2 hstack[s] = h;
+ 1177 2 vvstack[s] = vv;
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 23
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1178 2 hhstack[s] = hh;
+ 1179 2 wstack[s] = w;
+ 1180 2 zstack[s] = z;
+ 1181 2 break;
+ 1182 2
+ 1183 2 case pop:
+ 1184 2 if (s == 0) {
+ 1185 3 printf(bad_DVI_message);
+ 1186 3 return(1);
+ 1187 3 }
+ 1188 2 if (vv != vvstack[s]) vpset = 0;
+ 1189 2 x = xstack[s];
+ 1190 2 y = ystack[s];
+ 1191 2 v = vstack[s];
+ 1192 2 h = hstack[s];
+ 1193 2 vv = vvstack[s];
+ 1194 2 hh = hhstack[s];
+ 1195 2 w = wstack[s];
+ 1196 2 z = zstack[s];
+ 1197 2 s--;
+ 1198 2 break;
+ 1199 2
+ 1200 2 /* Right now there are no \special's defined, so when we get a \special, we
+ 1201 2 just skip over it. */
+ 1202 2
+ 1203 2 four_cases(xxx1)
+ 1204 2 for (; p != 0; p--) getc(dvifile);
+ 1205 2 break;
+ 1206 2
+ 1207 2 case bop:
+ 1208 2
+ 1209 2 /* If we've done the required number of pages, we'll skip the rest of the
+ 1210 2 DVI file. If not, type the first parameter of bop on the user's terminal
+ 1211 2 the way TeX does, to give an indication of progress. */
+ 1212 2
+ 1213 2 if (num_pages == max_pages) return(2);
+ 1214 2 v = 0; vv = 0; h = 0; hh = 0;
+ 1215 2 vpset = 0; hh_old = 100000;
+ 1216 2 four_bytes;
+ 1217 2 if (num_pages%12 == 0) printf("\n");
+ 1218 2 printf(" [%ld]",lcx.ul);
+ 1219 2 num_pages++;
+ 1220 2 for (i = 0; i<40; i++) getc(dvifile);
+ 1221 2 break;
+ 1222 2
+ 1223 2 case eop:
+ 1224 2 fprintf(outfile,"\n\f");
+ 1225 2 ln3p = 0;
+ 1226 2 break;
+ 1227 2
+ 1228 2 /* Now we have to consider the cases for pure motion. */
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 24
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1229 2
+ 1230 2 four_cases(right1)
+ 1231 2 set_h(h+p);
+ 1232 2 break;
+ 1233 2 four_cases(x1)
+ 1234 2 x = p;
+ 1235 2 case x0:
+ 1236 2 set_h(h+x);
+ 1237 2 break;
+ 1238 2 four_cases(y1)
+ 1239 2 y = p;
+ 1240 2 case y0:
+ 1241 2 set_v(v+y);
+ 1242 2 break;
+ 1243 2 four_cases(w1)
+ 1244 2 w = p;
+ 1245 2 case w0:
+ 1246 2 set_h(h+w);
+ 1247 2 break;
+ 1248 2 four_cases(z1)
+ 1249 2 z = p;
+ 1250 2 case z0:
+ 1251 2 set_v(v+z);
+ 1252 2 break;
+ 1253 2 four_cases(down1)
+ 1254 2 set_v(v+p);
+ 1255 2 break;
+ 1256 2
+ 1257 2 }
+ 1258 1 return(0);
+ 1259 1 }
+ 1260
+ 1261 /* Set_curf sets the current font to external number p. Note that the
+ 1262 current font is maintained as an internal font number. */
+ 1263
+ 1264 int set_curf(p)
+ 1265 int p;
+ 1266 {
+ 1267 1 to_ext[nf] = p;
+ 1268 1 curf = 0;
+ 1269 1 while (to_ext[curf] != p) { curf++; }
+ 1270 1 }
+ 1271
+ 1272 /* Define_font processes a font definition from the DVI file. The TFM file
+ 1273 for the font is read at this point from the directory TEX$FONTS. */
+ 1274
+ 1275 int define_font(e)
+ 1276 int e;
+ 1277 {
+ 1278 1 int i;
+ 1279 1 unsigned char p,n;
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 25
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1280 1 if (e == MAXTEXFONTS) { printf("\n Too many fonts for Dvi2ln3");
+ 1281 2 return(1); }
+ 1282 1 txfa[nf] = malloc(sizeof(struct txf));
+ 1283 1 to_ext[nf] = e;
+ 1284 1 four_bytes;
+ 1285 1 four_bytes;
+ 1286 1 txfa[nf] -> scaled_size = lcx.l;
+ 1287 1 four_bytes;
+ 1288 1 txfa[nf] -> design_size = lcx.l;
+ 1289 1 for (i=0; i<256; i++) txfa[nf] -> chu[i] = '\0';
+ 1290 1 p = getc(dvifile); n = getc(dvifile);
+ 1291 1 font_name[nf] = malloc(p+n+1);
+ 1292 1 for (i=0; i<p+n; i++) { font_name[nf][i] = getc(dvifile); }
+ 1293 1 font_name[nf][p+n] = '\0';
+ 1294 1
+ 1295 1 if (txfa[nf] -> scaled_size <= 0 || txfa[nf] -> scaled_size >=
+ 1296 1 8*8*8*8*8*8*8*8*8) {
+ 1297 2 printf("\n Font %s has a bad scaled size",font_name[nf]);
+ 1298 2 return(1);
+ 1299 2 }
+ 1300 1 if (txfa[nf] -> design_size <= 0 || txfa[nf] -> design_size >=
+ 1301 1 8*8*8*8*8*8*8*8*8) {
+ 1302 2 printf("\n Font %s has a bad design size",font_name[nf]);
+ 1303 2 return(1);
+ 1304 2 }
+ 1305 1
+ 1306 1 /* We follow DVItype and compute for each font a "space" parameter which is
+ 1307 1 one-sixth of the scaled design size. This parameter is used in rounding the
+ 1308 1 horizontal position to pixels according to "Stanford rules." See the
+ 1309 1 function set_h below. */
+ 1310 1
+ 1311 1 txfa[nf] -> space = txfa[nf] -> scaled_size/6;
+ 1312 1
+ 1313 1 i = read_tfm_file();
+ 1314 1 if (i != 0) return(i);
+ 1315 1 nf++;
+ 1316 1 return(0);
+ 1317 1
+ 1318 1 }
+ 1319
+ 1320 /* TFM files, like the DVI file, are read with getc, foolish as that may
+ 1321 seem. We use the same overlays that are used for merging DVI bytes into
+ 1322 longwords. However, since the TFM file consists of longwords, only
+ 1323 tfm_longword is need. [[This is also VAX dependent. It would not be
+ 1324 possible to use stdio's longword reading function getw here, because the
+ 1325 bytes have to be reversed.]] */
+ 1326
+ 1327 #define tfm_longword { lcx.uc[3] = getc(tfmfile); \
+ 1328 lcx.uc[2] = getc(tfmfile); lcx.uc[1] = getc(tfmfile); \
+ 1329 lcx.uc[0] = getc(tfmfile); }
+ 1330
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 26
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1331 /* Read_tfm_file obtains the character widths from the TFM file
+ 1332 corresponding to font nf. */
+ 1333
+ 1334 int read_tfm_file() {
+ 1335 1
+ 1336 1 int i,lh,nw;
+ 1337 1 int info[256];
+ 1338 1 long z,alpha,beta;
+ 1339 1 long width[256];
+ 1340 1
+ 1341 1 open_tfm_file();
+ 1342 1 if (tfmfile == NULL) {
+ 1343 2 printf("\n Can't open TFM file for font %s",font_name[nf]);
+ 1344 2 return(1); }
+ 1345 1
+ 1346 1 /* The TFM format is described in some issue of the TUGBoat (TeX users'
+ 1347 1 group newsletter), and in the comments to the program TFtoPL. Here we
+ 1348 1 summarize those aspects of TFM format that are relevant to the task of
+ 1349 1 extracting the widths of the characters.
+ 1350 1
+ 1351 1 The first 24 bytes of a TFM file contain twelve 16-bit integers that give
+ 1352 1 the lengths of the various subsequent portions of the file. The ones
+ 1353 1 relevant to our purposes are LH, length of the header data, BC, the
+ 1354 1 smallest character code in the font, EC, the largest character code in the
+ 1355 1 font, and NW, number of words in the width table. So, we read those right
+ 1356 1 now, and then skip over the remainder, and then over a set of LH longwords
+ 1357 1 called the "header." */
+ 1358 1
+ 1359 1 tfm_longword;
+ 1360 1 lh = 256*lcx.uc[1] + lcx.uc[0];
+ 1361 1 tfm_longword;
+ 1362 1 txfa[nf] -> bc = 256*lcx.uc[3] + lcx.uc[2];
+ 1363 1 txfa[nf] -> ec = 256*lcx.uc[1] + lcx.uc[0];
+ 1364 1 tfm_longword;
+ 1365 1 nw = 256*lcx.uc[3] + lcx.uc[2];
+ 1366 1 if (txfa[nf] -> bc > 255 || txfa[nf] -> ec > 255 ||
+ 1367 1 txfa[nf] -> bc > txfa[nf] -> ec || nw > 256) {
+ 1368 2 printf("\n Bad TFM file for font %s",font_name[nf]);
+ 1369 2 return(1);
+ 1370 2 }
+ 1371 1
+ 1372 1 for(i=0; i<lh+3; i++) tfm_longword;
+ 1373 1
+ 1374 1 /* After the header, there are two arrays in the TFM file that interest us.
+ 1375 1 The first, INFO, is EC-BC+1 longwords long and contains pointers to the
+ 1376 1 second, WIDTH, which is NW longwords long. For each character i, the width
+ 1377 1 of i is WIDTH[INFO[i-BC]]. We read these arrays into memory. */
+ 1378 1
+ 1379 1 for(i=0; i<txfa[nf] -> ec-txfa[nf] -> bc+1; i++) {
+ 1380 2 tfm_longword;
+ 1381 2 if (lcx.uc[3] >= nw) {
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 27
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1382 3 printf("\n Bad TFM file for font %s",font_name[nf]);
+ 1383 3 return(1);
+ 1384 3 }
+ 1385 2 info[i] = lcx.uc[3];
+ 1386 2 }
+ 1387 1
+ 1388 1 /* The widths are stored in a rather strange format known as a "fix-word."
+ 1389 1 A nonnegative width is expressed in "fix-word" format by expressing it in
+ 1390 1 units of 2^-20 times the design size. A negative width is expressed in
+ 1391 1 "fix-word" format by expressing its negative in those units, and then
+ 1392 1 changing the most significant byte to 255.
+ 1393 1
+ 1394 1 One needs to convert these widths into DVI units, multiplying by the scaled
+ 1395 1 design size according to a certain arcane algorithm. The algorithm is
+ 1396 1 copied from DVItype, to which we refer the reader for an explanation. */
+ 1397 1
+ 1398 1 z = txfa[nf] -> scaled_size;
+ 1399 1 alpha = 16*z; beta = 16;
+ 1400 1 while (z >= 4*(8*8*8*8*8*8*8)) { z = z/2; beta = beta/2; }
+ 1401 1 for(i=0; i<nw; i++) {
+ 1402 2 tfm_longword;
+ 1403 2 width[i] = ( ( (lcx.uc[0]*z)/256 + lcx.uc[1]*z )/256 +
+ 1404 2 lcx.uc[2]*z)/beta;
+ 1405 2 if (lcx.uc[3] == 255) width[i] -= alpha;
+ 1406 2 }
+ 1407 1
+ 1408 1 fclose(tfmfile);
+ 1409 1
+ 1410 1 /* Using these two arrays, we now compute the widths of the characters in
+ 1411 1 the fonts and place them into two malloc'ed arrays, one for the widths in
+ 1412 1 DVI units, one for the width in pixel units. */
+ 1413 1
+ 1414 1 font_width[nf] = malloc(4*(txfa[nf] -> ec+1));
+ 1415 1
+ 1416 1 for (i=0; i<txfa[nf] -> bc; i++) font_width[nf][i] = 0;
+ 1417 1 for (i=txfa[nf] -> bc; i <= txfa[nf] -> ec; i++)
+ 1418 1 font_width[nf][i] = width[info[i-txfa[nf] -> bc]];
+ 1419 1 return(0);
+ 1420 1 }
+ 1421
+ 1422 /* Open_tfm_file finds and opens the tfm file corresponding to a font nf.
+ 1423 The global file variable tfmfile is used to hold the file pointer. [[This
+ 1424 function will not work if the TFM file lies over the net on a VMS V3.x
+ 1425 host, because C file opens do not work in such circumstances. The problem
+ 1426 can be ignored, because eventually there should be no VMS V3.x hosts
+ 1427 left.]] */
+ 1428
+ 1429 int open_tfm_file () {
+ 1430 1
+ 1431 1 int jext,jnam;
+ 1432 1 char filespec[FILESPECLEN];
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 28
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1433 1
+ 1434 1 find_VMS_filename(font_name[nf],&jnam,&jext);
+ 1435 1 filespec[0] = '\0';
+ 1436 1
+ 1437 1 /* If there is no directory part, fill in using the logical tex$fonts */
+ 1438 1
+ 1439 1 if (jnam == 0) strcpy(filespec,"tex$fonts:");
+ 1440 1 strcat(filespec,font_name[nf]);
+ 1441 1
+ 1442 1 /* if there is no extension, add the extension ".tfm" */
+ 1443 1
+ 1444 1 if (font_name[nf][jext] == '\0') strcat(filespec,".tfm");
+ 1445 1 tfmfile = fopen(filespec,"r");
+ 1446 1
+ 1447 1 }
+ 1448
+ 1449 /* Define_font_pass2 skips a font definition from the DVI file. */
+ 1450
+ 1451 int define_font_pass2()
+ 1452 {
+ 1453 1 unsigned char p,n;
+ 1454 1 int i;
+ 1455 1 four_bytes;
+ 1456 1 four_bytes;
+ 1457 1 four_bytes;
+ 1458 1 p = getc(dvifile); n = getc(dvifile);
+ 1459 1 for (i=0; i<p+n; i++) getc(dvifile);
+ 1460 1 }
+ 1461
+ 1462 /* Do_rule writes into the ln3 file the escape sequence corresponding
+ 1463 to a rule. */
+ 1464
+ 1465 int do_rule(xx0,yy0,xx1,yy1)
+ 1466 int xx0,yy0,xx1,yy1;
+ 1467 {
+ 1468 1 int j;
+ 1469 1
+ 1470 1 xx0 = min(xx0+hoff,maxrmar);
+ 1471 1 xx1 = min(xx1+hoff,maxrmar);
+ 1472 1 xx0 = max(xx0,0);
+ 1473 1 xx1 = max(xx1,0);
+ 1474 1 yy0 = min(yy0+voff,maxbmar);
+ 1475 1 yy1 = min(yy1+voff,maxbmar);
+ 1476 1 yy0 = max(yy0,0);
+ 1477 1 yy1 = max(yy1,0);
+ 1478 1 if (xx0 > xx1) { j = xx0; xx0 = xx1; xx1 = j; }
+ 1479 1 if (yy0 > yy1) { j = yy0; yy0 = yy1; yy1 = j; }
+ 1480 1
+ 1481 1 if ((yy1 != yy0 && xx1 != xx0)) {
+ 1482 2 fprintf(outfile,"\033[1;%d;%d;%d;%d!|",
+ 1483 2 xx0,yy0,yy1-yy0,xx1-xx0);
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 29
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+ 1484 2 inc_ln3p(25);
+ 1485 2 }
+ 1486 1 }
+ 1487
+ 1488 /* Set_v is called to execute vertical-position-altering commands, other
+ 1489 than pops. It modifies v and vv, and outputs the vertical position to the
+ 1490 LN3 file. We don't follow Stanford rules here. They sometimes set the
+ 1491 vertical position in pixels vv to something other than the rounded value of
+ 1492 v. Not following Stanford rules implies, for example, that the relative
+ 1493 vertical positions of an accent and its accentee may differ by one pixel
+ 1494 according to how the baseline of the accentee gets rounded. [[This should
+ 1495 perhaps be fixed.]] */
+ 1496
+ 1497 int set_v(v1)
+ 1498 int v1;
+ 1499 {
+ 1500 1 int l;
+ 1501 1
+ 1502 1 l = pixel_round(v1);
+ 1503 1 v = v1;
+ 1504 1 vv = l;
+ 1505 1 vpset = 0;
+ 1506 1 }
+ 1507
+ 1508 /* Set_h is called whenever a DVI command is encountered that alters the
+ 1509 horizonal position, other than a set_char, set_rule or pop. It sets h to
+ 1510 the new value, and alters hh according to Stanford rules, printing the new
+ 1511 hh in the LN3 file. */
+ 1512
+ 1513 int set_h(new_h)
+ 1514 int new_h;
+ 1515 {
+ 1516 1 int l,old_hh;
+ 1517 1
+ 1518 1 old_hh = hh;
+ 1519 1 l = pixel_round(new_h);
+ 1520 1 if (txfa[curf] == 0 || new_h-h >= txfa[curf] -> space
+ 1521 1 || new_h-h <= -4*txfa[curf] -> space) hh = l;
+ 1522 1 else {
+ 1523 2 hh += pixel_round(new_h-h);
+ 1524 2 if (hh-l > MAX_DRIFT) hh = l+MAX_DRIFT;
+ 1525 2 else if (hh-l < -MAX_DRIFT) hh = l-MAX_DRIFT;
+ 1526 2 }
+ 1527 1 h = new_h;
+ 1528 1 return(0);
+ 1529 1 }
+
+
+
+Command Line
+------------
+
+DVI2LN3 14-OCT-1985 13:19:13 VAX C V2.0-003 Page 30
+V1.0 14-OCT-1985 13:19:01 USER$4:[TEX.LN03.SOURCES]DVI2LN3.C;40 (1)
+
+
+CC/DEB/LIS DVI2LN3
+
diff --git a/dviware/ln03/rose/dvi2ln3nft.c b/dviware/ln03/rose/dvi2ln3nft.c
new file mode 100644
index 0000000000..dd6e0983b1
--- /dev/null
+++ b/dviware/ln03/rose/dvi2ln3nft.c
@@ -0,0 +1,1145 @@
+/* This file contains those portions of Dvi2ln3 that create font loads. It
+is still being developed...
+
+Copyright (c) 1983, 1984, 1985, 1986 by Digital Equipment Corporation.
+Author: Flavio Rose, ...!decwrl!dec-rhea!dec-dvinci!rose.
+
+Development chronology:
+
+11/28/84: Initial version in PL/I. Finished 11/30/84.
+ 3/4/85: C translation, a prelude to writing DVI2LN3.
+ 5/7/85: Finally got around to debugging the C translation!
+ 6/5/85: Convert to work with Dvi2ln3 -- minor changes.
+ 6/18/85: Try not to use big input array.
+ 6/22/85: Cleanup, try to identify longs.
+11/12/85: Changed to read PXLs rather than LN03 format files. Old file
+ called dvi2ln3nft, now renamed to dvi2ln3pxl.
+11/15/85: More optimized PXL reading -- use lnf instead of the pxlbuf
+ when reading rasters. Return just status value from add_txf_...,
+ use ras_len_added to communicate size. Facilitate error
+ recovery with copy_ersatz_char. LN03 file reading functionality
+ woven back in.
+ 1/16/86: Embarrassing bug, forgot to close the nftf after done with it.
+ 3/28/86: Conditionalization for Ultrix.
+ 4/7/86: After moving this to 4.3bsd at MIT, a couple more
+ fixes to the portability.
+
+*/
+
+/* INTRODUCTION: When writing an LN03 driver for TeX, it must be kept in
+mind that the LN03 has limited font storage capability. The standard LN03
+has just 27k bytes, and two 128k byte RAM cartridges may be added.
+
+For this reason, when we convert a DVI file to LN03 form, we build a
+special font load that has just the glyphs that the DVI file needs. This
+allows very complex files, e.g. the first nine chapters of the
+Metafontbook, to be printed on LN03s that have just one extra RAM
+cartridge. This approach does, however, consume considerable resources in
+the VAX to build those font loads. */
+
+/* The following is customary boilerplate. */
+
+#ifdef vms
+#include stdio
+#else
+#include <stdio.h>
+#endif
+
+/* In VMS we declare external variables to be globalref.
+This is not really necessary, just an old habit. */
+
+#ifdef vms
+#define GLOBAL globaldef
+#define EXTERN globalref
+#else
+#define GLOBAL
+#define EXTERN extern
+#endif
+
+/* The following symbols are declared compatibly with those in the file
+Dvi2ln3.c. */
+
+#define maxnfonts 31
+#define MAXTEXFONTS 100
+#define FILESPECLEN 252
+#define leftfirst 33
+#define leftlast 126
+#define rightfirst 161
+#define rightlast 254
+
+EXTERN FILE *outfile;
+EXTERN int lastch[maxnfonts];
+EXTERN unsigned char chw[maxnfonts][256];
+
+struct txf { unsigned char chu[256]; int bc, ec; long space, design_size,
+ scaled_size; int nchs; };
+EXTERN struct txf *txfa[MAXTEXFONTS+1];
+EXTERN long int *font_width[MAXTEXFONTS+1];
+
+EXTERN char *font_name[MAXTEXFONTS+1];
+EXTERN float conv, unmag_conv;
+
+#ifndef vms
+char *getenv();
+#endif
+
+/* The LN03 font load we build is buffered in a fixed-size area, slightly
+smaller than the maximum amount of font memory available on an LN03. [[The
+code should be changed to chain fixed-size buffers, so font loads of
+unlimited size can be handled, subject only to memory availability. On
+systems with limited memory, it may be desirable to use a disk file instead
+of this buffer.]] */
+
+#define OBUFLEN 2*256*256
+GLOBAL unsigned char lnf[OBUFLEN];
+
+/* While constructing the font load, we keep track of the length of the
+"character definitions area" in ras_len. We also have to keep track of the
+raster size measured in three different ways, as psize (portrait), lsize
+(landscape), and msize (mixed). This is required to fill certain fields in
+the font load. */
+
+GLOBAL long int ras_len,psize,lsize,msize;
+
+/* The font load that we construct consists of a number of LN03 fonts. Each
+of these contains up to 94 glyphs. They may be "left fonts", so their
+glyphs are invoked with codes 33 through 126; or they may be "right fonts"
+that respond to codes 161 through 254.
+
+The font load that we construct is made up of alternating left and right
+fonts (sometimes called "font pairs"). A TeX font (represented by a txf
+record) is added to a font pair. It may be split between the left and right
+halves of the pair.
+
+The variable using_r keeps track of whether we are using a right font. */
+
+GLOBAL char using_r;
+
+GLOBAL unsigned int nft_first_ch,nft_last_ch,chardir_offset;
+GLOBAL int lnf_chardir,ras_beg;
+GLOBAL long ras_len_added;
+
+GLOBAL int pxlf,nftf;
+
+/* The purpose of add_txf_to_lnf is to read the PXL or LN03 font file
+corresponding to TeX font txfno and add its rasters to the font pair
+referred to by lnfno. The function returns 0 if all went well, and various
+negative numbers to indicate problems. The number of bytes added to the
+font load is returned in the global ras_len_added. */
+
+int add_txf_to_lnf(lnfno,txfno)
+int lnfno,txfno;
+{
+ int i,j,k,l,width;
+ int first_txfc,last_txfc;
+ char first_txf,last_txf;
+
+ ras_len_added = 0;
+
+ if (txfno < 0 || txfno > MAXTEXFONTS || txfa[txfno] == 0 ||
+ lnfno < 0 || lnfno > maxnfonts) {
+ printf("\nDvi2ln3 internal error, bad txfno or lnfno");
+ goto error_return;
+ }
+
+/* Now, compute first_txfc and last_txfc, the first and last characters
+codes to be copied from the TeX font. */
+
+ first_txfc = -1; last_txfc = -1;
+ for (i=0; i<256; i++) {
+ if (txfa[txfno] -> chu[i] != 0) {
+ if (first_txfc == -1) first_txfc = i;
+ last_txfc = i;
+ }
+ }
+ if (first_txfc == -1 || last_txfc == -1) return(0);
+
+/* Tell user what font is being loaded */
+
+ printf("\nLoading font %s",font_name[txfno]);
+ k = (1500.0*conv*txfa[txfno] -> scaled_size)/
+ (unmag_conv*txfa[txfno] -> design_size) + 0.5;
+ l = ((float)k)/15.0 + 0.5;
+ if (l != 100) printf(" (magnified %d%%)",l);
+
+/* Now open the file from which we are getting rasters. We look first for a
+PXL file, then for an LN03 format font file (NFT file). */
+
+ pxlf = -1;
+ nftf = -1;
+ open_nft_file(txfno);
+ if (nftf == -1) open_pxl_file(txfno);
+
+/* If neither a PXL file nor an LN03 format file could be opened, warn the
+user. The font load will be filled in with blanks nonetheless. */
+
+ if (pxlf == -1 && nftf == -1)
+ printf("\n Couldn't find good font file: using blanks");
+
+/* Now replace first_txfc and last_txfc by the character codes into which
+they are going to be copied. */
+
+ first_txfc = txfa[txfno] -> chu[first_txfc];
+ last_txfc = txfa[txfno] -> chu[last_txfc];
+
+/* Determine if this is the first or last TeX font that is being copied
+into the current LN03 font pair. */
+
+ first_txf = (first_txfc == leftfirst);
+ last_txf = (last_txfc == lastch[lnfno]);
+
+/* If this is the first TeX font for the current LN03 font pair, then we
+have to initialize the LN03 font load buffer. The initialization routine
+computes where the rasters begin in the output buffer, puts in the blanks,
+etc. */
+
+ if (first_txf) {
+ using_r = 0;
+ initialize_lnf(lnfno);
+ }
+
+/* Now go through all the characters in the TeX font and copy their rasters
+into the appropriate positions in the LN03 font pair. When we copy the the
+last rasters into the left font, we write the font out and then a comma to
+separate it from the next font. [[Fencepost error?]] */
+
+ for (i=0; i<256; i++) {
+ if (txfa[txfno] -> chu[i] != 0) {
+ j = txfa[txfno] -> chu[i];
+ if (!using_r && j >= rightfirst) {
+ write_lnf();
+ fprintf(outfile,"\n,");
+ using_r = 1;
+ initialize_lnf(lnfno);
+ }
+ if (pxlf != -1) {
+ k = copy_pxl_char(i,j,lnfno);
+ if (k != 0) {
+ close(pxlf);
+ pxlf = -1;
+ }
+ } else if (nftf != -1) {
+ k = copy_nft_char(i,j,lnfno);
+ if (k != 0) {
+ close(nftf);
+ nftf = -1;
+ }
+ }
+ if (pxlf == -1 && nftf == -1) {
+ width = font_width[txfno][i]*conv+0.5;
+ k = copy_ersatz_char(j,lnfno,width);
+ }
+ }
+ }
+
+ if (nftf != -1) close(nftf);
+ if (pxlf != -1) close(pxlf);
+
+/* If this is the last TeX font being added to this LN03 font pair, write
+out the current font (right or left) from the pair. */
+
+ if (last_txf) write_lnf();
+
+/* Type and return the incremental amount of space that the TeX font being
+added takes up in the output font load. */
+
+ ras_len_added += 4*(txfa[txfno] -> nchs);
+ printf(", %d bytes",ras_len_added);
+
+ return(0);
+
+error_return:
+ return(-1);
+
+}
+
+/* A buffer is used to hold the directory part of the PXL file. */
+
+#define PXLBUFSIZE 4*517
+
+GLOBAL unsigned char pxlbuf[PXLBUFSIZE];
+GLOBAL long pxllen;
+
+/* The value pxlconv is computed to be the correct conversion factor from the
+widths in the PXL file, which are expressed in units of 2^-20 times the
+design size, to pixels. */
+
+GLOBAL float pxlconv;
+
+/* As usual when dealing with TeX files, we have to rearrange bytes in
+an overlay to combine them into longwords. The reason for this is that
+bytes in TeX files are combined into longwords the opposite way from how
+the VAX combines them.
+
+Hence, the following overlay and macro: */
+
+GLOBAL union lc { unsigned long int ul;
+ long int l;
+ unsigned char c[4]; } lcy;
+
+#define copy_from_lnf(i) lcy.c[0] = lnf[i]; lcy.c[1] = lnf[i+1]; \
+ lcy.c[2] = lnf[i+2]; lcy.c[3] = lnf[i+3]
+#define copy_to_lnf(i) lnf[i] = lcy.c[0]; lnf[i+1] = lcy.c[1]; \
+ lnf[i+2] = lcy.c[2] ; lnf[i+3] = lcy.c[3]
+#define copy_from_pxl(_x) { lcy.c[3] = pxlbuf[_x]; \
+ lcy.c[2] = pxlbuf[(_x)+1]; lcy.c[1] = pxlbuf[(_x)+2]; \
+ lcy.c[0] = pxlbuf[(_x)+3]; }
+
+/* open_pxl_file reads the last 517 longwords of the PXL file into the
+pxlbuf. */
+
+int open_pxl_file(txfno)
+int txfno;
+{
+
+ long i,j;
+ float two_to_the_20th = 0x100000;
+ int k,l,jnam,jext,target;
+ char fullspec[FILESPECLEN];
+#ifndef vms
+ char *pxldir;
+#endif
+
+ find_filename(font_name[txfno],&jnam,&jext);
+
+ fullspec[0] = '\0';
+#ifdef vms
+ if (jnam == 0) strcpy(fullspec,"TEX$PXLDIR:");
+#else
+ if (jnam == 0) {
+ pxldir = getenv("TEX_PXLDIR");
+ strcpy(fullspec,pxldir);
+ strcat(fullspec,"/");
+ }
+#endif
+ strcat(fullspec,font_name[txfno]);
+ k = (1500.0*conv*txfa[txfno] -> scaled_size)/
+ (unmag_conv*txfa[txfno] -> design_size) + 0.5;
+ l = strlen(fullspec);
+ sprintf(&fullspec[l],".%dpxl\0",k);
+
+ pxllen = 0;
+ pxlf = open(fullspec,0);
+ if (pxlf == -1) return(1);
+
+ lseek(pxlf,-517*4,2);
+ i = read(pxlf,pxlbuf,517*4);
+ if (i != 517*4) {
+ printf("\n Error while reading PXL file");
+ close(pxlf);
+ pxlf = -1;
+ return(1);
+ }
+ pxllen = 4*517;
+
+/* Now that we have read the PXL file, check that it is in correct format
+by looking for a trailing ID byte of 1001. */
+
+ copy_from_pxl(pxllen-4);
+ if (lcy.ul != 1001) {
+ printf("\n Error while reading PXL file");
+ close(pxlf);
+ pxlf = -1;
+ return(1);
+ }
+
+/* If the format is correct, derive a scale factor for the widths. */
+
+ copy_from_pxl(pxllen-12);
+ pxlconv = lcy.ul;
+ copy_from_pxl(pxllen-16);
+ pxlconv = (pxlconv/two_to_the_20th)*(lcy.ul/5.0)
+ *(1.0/(72.27*two_to_the_20th));
+ return(0);
+
+}
+
+GLOBAL unsigned char rev_byte[256] = {
+ 0,128,64,192,32,160,96,224,
+ 16,144,80,208,48,176,112,240,
+ 8,136,72,200,40,168,104,232,
+ 24,152,88,216,56,184,120,248,
+ 4,132,68,196,36,164,100,228,
+ 20,148,84,212,52,180,116,244,
+ 12,140,76,204,44,172,108,236,
+ 28,156,92,220,60,188,124,252,
+ 2,130,66,194,34,162,98,226,
+ 18,146,82,210,50,178,114,242,
+ 10,138,74,202,42,170,106,234,
+ 26,154,90,218,58,186,122,250,
+ 6,134,70,198,38,166,102,230,
+ 22,150,86,214,54,182,118,246,
+ 14,142,78,206,46,174,110,238,
+ 30,158,94,222,62,190,126,254,
+ 1,129,65,193,33,161,97,225,
+ 17,145,81,209,49,177,113,241,
+ 9,137,73,201,41,169,105,233,
+ 25,153,89,217,57,185,121,249,
+ 5,133,69,197,37,165,101,229,
+ 21,149,85,213,53,181,117,245,
+ 13,141,77,205,45,173,109,237,
+ 29,157,93,221,61,189,125,253,
+ 3,131,67,195,35,163,99,227,
+ 19,147,83,211,51,179,115,243,
+ 11,139,75,203,43,171,107,235,
+ 27,155,91,219,59,187,123,251,
+ 7,135,71,199,39,167,103,231,
+ 23,151,87,215,55,183,119,247,
+ 15,143,79,207,47,175,111,239,
+ 31,159,95,223,63,191,127,255
+ };
+
+#define pxl_word(_x) (pxlbuf[_x]*256+pxlbuf[(_x)+1])
+#define signed_pxl_word(_x) ((pxl_word(_x)>0x8000) ? \
+ (pxl_word(_x)-0x10000) : pxl_word(_x))
+
+/* Copy_pxl_char copies the rasters and character directory information
+corresponding to code from in the PXL file, into the to position of the
+LN03 font load buffer. */
+
+int copy_pxl_char(from,to,lnfno)
+unsigned char from,to;
+int lnfno;
+{
+
+ unsigned long ds,rs,i,j,def_start;
+ unsigned int rows,cols,k,l,m,n;
+ int xoffset, yoffset;
+ int width;
+ char all_blank;
+
+/* Check if the character code from is in the right range for the PXL file.
+*/
+
+ if (from > 127) return(1);
+
+/* Locate the definition and the rasters for from in the PXL file. */
+
+ ds = 16*from;
+ copy_from_pxl(ds+8);
+ rs = 4*lcy.ul;
+ cols = pxl_word(ds); rows = pxl_word(ds+2);
+
+/* If a glyph has no rasters, an "undocumented feature" of the LN03 seems
+to cause the glyph to be printed incorrectly. Because of this, we set the
+number of rows and columns to 1, and put in a blank byte (below). */
+
+ all_blank = ((rows == 0) & (cols == 0)) | rs == 0;
+ if (all_blank) {
+ cols = 1;
+ rows = 1;
+ }
+
+/* Compute the width of the glyph in pixels, the xoffset, and the yoffset.
+*/
+
+ copy_from_pxl(ds+12);
+ width = pxlconv*lcy.ul+0.5;
+ xoffset = signed_pxl_word(ds+4);
+ yoffset = signed_pxl_word(ds+6);
+ chw[lnfno][to] = width;
+
+/* Check that we have enough room left in the LN03 font load buffer. Fudge
+factor of 3 added because the read below may read too much. */
+
+ def_start = ras_beg+ras_len;
+ if (def_start+rows*((cols+7)/32)+24+3 > OBUFLEN) {
+ printf("\nNo more room in LN03 font load buffer (%ld bytes long)",
+ OBUFLEN);
+ return(1);
+ }
+
+/* Clear the character definition area in the LN03 font load buffer. */
+
+ for(i=0; i<24+rows*((cols+7)/8); i++)
+ lnf[def_start+i] = 0;
+
+/* Set the fields in the first six longwords of the LN03 font load
+character definition. The first assignment sets the so-called "flag flag",
+which must be always be set according to the Common Font File Format. A
+conversion factor of 24 is used in converting pixel values, because the
+values are supposed to be in centipoints in the LN03 font load, and if we
+assume there are 300 pixels in an inch, then there are 7200 centipoints in
+an inch. */
+
+ lnf[def_start+3] = 0x80;
+ lcy.ul = 24*width;
+ copy_to_lnf(def_start+4);
+ lcy.l = -24*xoffset;
+ copy_to_lnf(def_start+8);
+ lcy.l = -24*yoffset;
+ copy_to_lnf(def_start+12);
+
+/* The rasters are always placed in portrait into the LN03 font load, with
+no use of run-length encoding. Thus, the orient field in the raster format
+will be 0, and the Type 1 field is set to 0x81. */
+
+ lnf[def_start+17] = 0x81;
+ lnf[def_start+20] = rows%256;
+ lnf[def_start+21] = rows/256;
+ lnf[def_start+22] = cols%256;
+ lnf[def_start+23] = cols/256;
+
+/* Now we copy the rasters themselves. As we do so, we have to reverse
+the bits within each byte. */
+
+ k = (cols+31)/32;
+ n = (cols+7)/8;
+ lseek(pxlf,rs,0);
+ if (!all_blank) {
+ for (l=0; l<rows; l++) {
+ read(pxlf,&lnf[def_start+24+n*l],4*k);
+ }
+ for (m=0; m<n*rows; m++)
+ lnf[def_start+24+m] = rev_byte[lnf[def_start+24+m]];
+ }
+
+/* Set the character locator, increment the LN03 font load buffer length,
+and recompute the three sizes. */
+
+ lcy.l = def_start;
+ if (to <= leftlast) i = to-leftfirst; else i = to-rightfirst;
+ copy_to_lnf(lnf_chardir+4*i);
+ ras_len += 24+rows*n;
+ ras_len_added += 24+rows*n;
+ if (ras_len%2 == 1) {
+ lnf[ras_beg+ras_len] = 0;
+ ras_len++;
+ ras_len_added++;
+ }
+
+ i = rows*((cols+7)/8);
+ psize += i;
+ j = ((rows+7)/8)*cols;
+ lsize += j;
+ msize += (i > j) ? i : j;
+
+ return(0);
+}
+
+/* Write a blank character of specified width (in pixels) into LN03 font
+load buffer. Also, set the chw array. [[This function is a bit excessive.
+If we couldn't find a character's rasters, it would suffice just to leave
+the character locator blank.]] */
+
+int copy_ersatz_char(to,lnfno,width)
+unsigned char to;
+int lnfno,width;
+{
+
+ unsigned long ds,rs,i,j,def_start;
+ unsigned int rows,cols,k,l,m,n;
+
+ cols = 1;
+ rows = 1;
+ chw[lnfno][to] = width;
+
+/* Check that we have enough room left in the LN03 font load buffer. Return
+silently if not. */
+
+ def_start = ras_beg+ras_len;
+ if (def_start+rows*((cols+7)/32)+24 > OBUFLEN) return(1);
+
+/* Clear the character definition area in the LN03 font load buffer. */
+
+ for(i=0; i<24+rows*((cols+7)/8); i++)
+ lnf[def_start+i] = 0;
+ lnf[def_start+3] = 0x80;
+ lcy.ul = 24*width;
+ copy_to_lnf(def_start+4);
+
+/* The rasters are always placed in portrait into the LN03 font load, with
+[no use of run-length encoding. Thus, the orient field in the raster format
+will be 0, and the Type 1 field is set to 0x81. */
+
+ lnf[def_start+17] = 0x81;
+ lnf[def_start+20] = rows%256;
+ lnf[def_start+21] = rows/256;
+ lnf[def_start+22] = cols%256;
+ lnf[def_start+23] = cols/256;
+
+/* Set the character locator, increment the LN03 font load buffer length,
+and recompute the three sizes. */
+
+ n = (cols+7)/8;
+ lcy.l = def_start;
+ if (to <= leftlast) i = to-leftfirst; else i = to-rightfirst;
+ copy_to_lnf(lnf_chardir+4*i);
+ ras_len += 24+rows*n;
+ ras_len_added += 24+rows*n;
+ if (ras_len%2 == 1) {
+ lnf[ras_beg+ras_len] = 0;
+ ras_len++;
+ ras_len_added++;
+ }
+
+ i = rows*((cols+7)/8);
+ psize += i;
+ j = ((rows+7)/8)*cols;
+ lsize += j;
+ msize += (i > j) ? i : j;
+
+ return(0);
+}
+
+/* The opening part of each NFT file is copied into the array nfta.
+Nftlen keeps track of how much of the NFT file we have read. Nftf
+is the file descriptor used to do the reading. */
+
+GLOBAL unsigned char nfta[480+1024];
+GLOBAL long nftlen;
+
+#define copy_from_nft(i) lcy.c[0] = nfta[i]; lcy.c[1] = nfta[i+1]; \
+ lcy.c[2] = nfta[i+2]; lcy.c[3] = nfta[i+3]
+
+/* Open_nft_file opens the NFT file and reads the opening part into the NFT
+array. */
+
+int open_nft_file(txfno)
+int txfno;
+{
+ int k,l,jnam,jext,target;
+ char fullspec[FILESPECLEN];
+#ifndef vms
+ char *ln3dir;
+#endif
+
+ find_filename(font_name[txfno],&jnam,&jext);
+
+ fullspec[0] = '\0';
+#ifdef vms
+ if (jnam == 0) strcpy(fullspec,"TEX$LN03DIR:");
+#else
+ if (jnam == 0) {
+ ln3dir = getenv("TEX_LN03DIR");
+ strcpy(fullspec,ln3dir);
+ strcat(fullspec,"/");
+ }
+#endif
+ strcat(fullspec,font_name[txfno]);
+ k = (100.0*conv*txfa[txfno] -> scaled_size)/
+ (unmag_conv*txfa[txfno] -> design_size) + 0.5;
+ if (k == 100) strcat(fullspec,".nft");
+ else {
+ l = strlen(fullspec);
+ if (k < 1000) sprintf(&fullspec[l],".%03d\0",k);
+ else sprintf(&fullspec[l],".%d\0",k);
+ }
+
+ nftlen = 0;
+ nftf = open(fullspec,0);
+ if (nftf == -1) return(1);
+
+ if (read_nft_with_retry(nfta,480) != 0) {
+ close(nftf);
+ nftf = -1;
+ return(1);
+ }
+
+/* The NFT file might be random, or it might be a Common Font File Format
+file that isn't loadable into an LN03. So, we make a few checks.
+
+The second longword must be 'FONT'. */
+
+ if (nfta[4] != 'F' || nfta[5] != 'O' || nfta[6] != 'N'
+ || nfta[7] != 'T') {
+ printf(" -- Bad NFT file, lacks FONT.");
+ close(nftf);
+ nftf = -1;
+ return(1);
+ }
+
+/* We also check that there are a full 16 bytes of character parameters. If
+there are fewer, we give up: LN03s need all sixteen, and it's too hard to
+figure out how to give the missing ones reasonable values.
+
+Throughout this program, we define some constants beginning with fnt__
+which define byte offsets into the nft file. [[The long prefixes of these
+may cause some trouble if one's C compiler does not recognize too many
+significant characters in identifiers.]] */
+
+#define fnt__l_parameters_size 200
+
+ copy_from_nft(fnt__l_parameters_size);
+ if (lcy.l != 16) {
+ printf(" -- bad NFT file, not enough character parameters");
+ close(nftf);
+ nftf = -1;
+ return(1);
+ }
+
+/* Set the variable that tells us the starting location of the
+character directory. */
+
+ chardir_offset = 480;
+
+/* Figure out what the first and last character codes in the NFT file are.
+*/
+
+#define fnt__l_first_character 164
+
+ copy_from_nft(fnt__l_first_character);
+ nft_first_ch = lcy.l;
+ copy_from_nft(fnt__l_first_character+4);
+ nft_last_ch = lcy.l;
+
+/* We must have 0 <= nft_first_ch <= nft_last_ch <= 255, otherwise we
+give up on the NFT file. */
+
+ if (!((0 <= nft_first_ch) && (nft_first_ch <= nft_last_ch) &&
+ (nft_last_ch <= 255))) {
+ printf(" -- bad NFT file, bad character range.");
+ close(nftf);
+ nftf = -1;
+ return(1);
+ }
+
+/* Now read the character directory into the nfta. */
+
+ target = chardir_offset+4*(nft_last_ch+1-nft_first_ch);
+ if (read_nft_with_retry(&nfta[480],target) != 0) {
+ close(nftf);
+ nftf = -1;
+ return(1);
+ }
+ return(0);
+}
+
+/* Copy_nft_char copies the character parameters and rasters corresponding
+to the from argument out of the NFT file and into the LN03 font buffer. It
+also updates the chw (character width) array. */
+
+int copy_nft_char(from,to,lnfno)
+unsigned char from,to;
+int lnfno;
+{
+
+ int rows,columns,quo;
+ long i,j,len,def_start,target;
+
+/* Replace from by the first character code if the NFT file has no rasters
+for code from. If this is done, the output will of course be mangled. The
+user has already been warned above, so no further message is needed. */
+
+ if (from < nft_first_ch) from = nft_first_ch;
+ if (from > nft_last_ch) from = nft_last_ch;
+
+/* First we need to determine def_start, the byte offset of the character
+definition for code from in the NFT file */
+
+ copy_from_nft(chardir_offset+(from-nft_first_ch)*4);
+ def_start = lcy.l;
+
+/* If the locator is earlier in the file, just give up. Otherwise, assume
+it's good, continue and give a vague error message is something goes wrong.
+*/
+
+ if (def_start < nftlen || read_nft_until(def_start) != 0) {
+ printf("\n LN03 font file bad, using blanks");
+ return(1);
+ }
+
+/* Read the first 24 bytes of character data. */
+
+ def_start = ras_beg+ras_len;
+ if (read_nft_with_retry(&lnf[def_start],nftlen+24) != 0) return(1);
+
+/* Now we need to check that the rasters are uncompressed. LN03s can't
+accept compressed rasters, and this program isn't clever enough to
+decompress them. */
+
+ if (lnf[def_start+17] != 129) {
+ printf("\n LN03 font file bad, using blanks");
+ return(1);
+ }
+
+/* Fill in the chw array (which holds character widths). Centipoint to
+pixel conversion is necessary (there are 24 centipoints in a pixel), with
+rounding (hence the 12). */
+
+ copy_from_lnf(def_start+4);
+ chw[lnfno][to] = (lcy.l+12)/24;
+
+/* NFT files may contain character widths which are not integral numbers of
+pixels. It is necessary to round such values to pixels when copying into
+the final font load -- if sent unrounded to the LN03, the widths will be
+truncated and positions will be off in the printed output. */
+
+ lcy.l = 24*chw[lnfno][to];
+ copy_to_lnf(def_start+4);
+
+/* Now we need to compute the size of the rasters, and copy that number of
+bytes into the LN03 font buffer. */
+
+ rows = lnf[def_start+20]+256*lnf[def_start+21];
+ columns = lnf[def_start+22]+256*lnf[def_start+23];
+
+/* We have to update psize, lsize and msize to fill slots in the format. */
+
+ quo = rows/8;
+ if (rows != quo*8) quo++;
+ i = quo*columns;
+ lsize += quo*columns;
+ quo = columns/8;
+ if (columns != quo*8) quo++;
+ psize += quo*rows;
+ if (i > quo*rows) msize += i; else msize += quo*rows;
+
+/* Now do the actual copy. We have to ensure that the rasters are
+word-aligned in the font load (where one word = 2 bytes, of course). */
+
+ if (lnf[def_start+16] % 2 == 0) len = quo*rows; else len = i;
+ if (read_nft_with_retry(&lnf[def_start+24],nftlen+len) != 0)
+ return(1);
+ lcy.l = def_start;
+ if (to <= leftlast) i = to-leftfirst; else i = to-rightfirst;
+ copy_to_lnf(lnf_chardir+4*i);
+ ras_len += len+24;
+ ras_len_added += len+24;
+ if (ras_len % 2 != 0) {
+ lnf[ras_beg+ras_len] = 0;
+ ras_len++;
+ ras_len_added++;
+ }
+ return(0);
+
+}
+
+int read_nft_with_retry(where,upto)
+long upto;
+char *where;
+{
+ long i,target;
+ int l;
+
+ i = 0;
+ while (nftlen < upto) {
+ target = upto-nftlen;
+ l = read(nftf,&where[i],target);
+ if (l <= 0) {
+ printf("\n LN03 font file bad, using blanks");
+ return(1);
+ }
+ nftlen += l;
+ i += l;
+ }
+ return(0);
+}
+
+int read_nft_until(pos)
+long pos;
+{
+ char bit_bucket[512];
+ int l;
+ long target;
+
+ while (nftlen < pos) {
+ target = pos-nftlen;
+ if (target > 512) target = 512;
+ l = read(nftf,bit_bucket,target);
+ if (l <= 0) {
+ printf("\n LN03 font file bad, using blanks");
+ return(1);
+ }
+ nftlen += l;
+ }
+ return(0);
+}
+
+GLOBAL unsigned char preline[96];
+
+/* write_lnf performs the last fixups to the LN03 font buffer and dumps it,
+sixelized, into the output file. */
+
+int write_lnf (){
+
+ long i,j,k,rem,quo,ma;
+
+/* We now have to fix a number of slots: size of file, length of rasters,
+final 'FONT', psize, lsize, msize. */
+
+ lcy.l = ras_beg+ras_len+8;
+ copy_to_lnf(0);
+ copy_to_lnf(lcy.l-8);
+ lnf[lcy.l-4] = 'F';
+ lnf[lcy.l-3] = 'O';
+ lnf[lcy.l-2] = 'N';
+ lnf[lcy.l-1] = 'T';
+
+/* Clear out remaining bytes up to 96 more. */
+
+ j = 96*((lcy.l+95)/96);
+ for (i=lcy.l; i<j; i++) lnf[i] = 0;
+
+/* Length of rasters */
+
+#define fnt__l_char_definitions_length 156
+
+ lcy.l = ras_len;
+ copy_to_lnf(fnt__l_char_definitions_length);
+
+#define fnt__l_portrait_byte_count 228
+#define fnt__l_landscape_byte_count 232
+#define fnt__l_mixed_byte_count 236
+
+ lcy.l = psize;
+ copy_to_lnf(fnt__l_portrait_byte_count);
+
+ lcy.l = lsize;
+ copy_to_lnf(fnt__l_landscape_byte_count);
+
+ lcy.l = msize;
+ copy_to_lnf(fnt__l_mixed_byte_count);
+
+/* Now sixelize and write the contents of LNF. */
+
+ sixelize_and_write(ras_beg+ras_len+8);
+ return(0);
+}
+
+int sixelize_and_write(howmuch)
+int howmuch;
+{
+ int quo,i,j;
+
+ quo = howmuch/96;
+ if (howmuch > quo*96) quo++;
+
+ for (i=0; i<quo; i++) {
+ for (j=0; j<96; j++)
+ preline[j] = lnf[96*i+j];
+ write_preline();
+ }
+
+}
+
+int write_preline() {
+
+ char sixel_line[129];
+ int i,j,ix,pllen;
+ unsigned v,w;
+
+ pllen = 96;
+ ix = 0;
+ sixel_line[128] = '\0';
+ for (i=0; i<128; i++) sixel_line[i] = '?';
+ for (i=0; i<pllen; i += 3) {
+ v = preline[i];
+ w = v>>2;
+ sixel_line[ix] = 63+w;
+ ix++;
+ w = (v&3)<<4;
+ v = preline[i+1];
+ w |= v>>4;
+ sixel_line[ix] = 63+w;
+ ix++;
+ w = (v&15)<<2;
+ v = preline[i+2];
+ w |= v>>6;
+ sixel_line[ix] = 63+w;
+ ix++;
+ w = v&63;
+ sixel_line[ix] = 63+w;
+ ix++;
+ }
+ fprintf(outfile,"\n%s",sixel_line);
+}
+
+/* Initializes the LN03 font buffer. */
+
+int initialize_lnf(lnfno)
+int lnfno;
+{
+ int i,j;
+
+/* The following array holds reasonable values for bytes 0-479 of an LN03
+format font. These bytes were copied from a font load which the LN03 was
+known to accept. The font loads generated by this program are initialized
+using these values. Some of them are overwritten later, however. */
+
+ static char good_opening[512] = {
+ 104,38,0,0,70,79,78,84,
+ 1,0,0,0,31,0,0,0,
+ 20,0,0,0,85,48,48,48,
+ 48,48,48,48,48,50,83,75,
+ 48,48,71,71,48,48,48,49,
+ 85,90,90,90,90,48,50,70,
+ 48,48,48,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 181,7,9,0,11,0,14,0,
+ 0,0,0,0,104,0,0,0,
+ 252,0,0,0,124,0,0,0,
+ 100,1,0,0,120,1,0,0,
+ 224,1,0,0,4,0,0,0,
+ 88,3,0,0,0,0,0,0,
+ 92,3,0,0,48,0,0,0,
+ 92,3,0,0,0,0,0,0,
+ 140,3,0,0,212,34,0,0,
+ 140,3,0,0,33,0,0,0,
+ 126,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,0,0,168,0,0,0,
+ 16,0,0,0,94,0,0,0,
+ 0,0,0,0,94,0,0,0,
+ 0,0,0,0,94,0,0,0,
+ 0,0,0,0,236,25,0,0,
+ 54,25,0,0,14,27,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2,0,0,0,
+ 7,0,0,0,92,3,0,0,
+ 7,0,0,0,99,3,0,0,
+ 16,0,0,0,106,3,0,0,
+ 16,0,0,0,122,3,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 10,0,0,0,244,1,0,0,
+ 0,0,24,0,16,0,0,0,
+ 16,0,0,0,1,0,1,0,
+ 1,0,1,0,0,0,1,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 30,0,0,0,20,0,0,0,
+ 196,255,255,255,20,0,0,0,
+ 106,255,255,255,20,0,0,0,
+ 0,0,1,0,0,0,30,0,
+ 166,255,255,255,0,0,0,0,
+ 40,0,0,0,0,0,0,0,
+ 60,0,0,0,240,0,0,0,
+ 60,0,0,0,100,0,0,0,
+ 240,0,0,0,120,0,0,0,
+ 40,0,0,0,120,0,0,0,
+ 96,255,255,255,136,255,255,255,
+ 186,255,255,255,240,0,0,0,
+ 76,255,255,255,60,0,0,0,
+ 160,0,0,0,120,0,0,0,
+ 20,0,0,0,20,0,0,0,
+ 140,3,0,0,194,3,0,0,
+ 244,3,0,0,160,4,0,0,
+ 254,4,0,0,162,5,0,0,
+ 46,6,0,0,84,6,0,0};
+ int pool_beg;
+
+/* The character directory always begins at position 480 in the Common
+Font File Format. */
+
+ lnf_chardir = 480;
+ ras_len = 0;
+ psize = 0;
+ lsize = 0;
+ msize = 0;
+
+/* Copy the good values into the lnf array */
+
+ for (i=0; i<480; i++)
+ lnf[i] = good_opening[i];
+
+/* Set the last character if necessary */
+
+#define fnt__l_first_character 164
+
+ if (lastch[lnfno] < leftlast && !using_r)
+ lnf[fnt__l_first_character+4] = lastch[lnfno];
+ if (using_r)
+ lnf[fnt__l_first_character+4] = lastch[lnfno]-rightfirst+
+ leftfirst;
+
+/* Set the character counts */
+
+#define fnt__l_infile_locators 204
+#define fnt__l_character_definitions 212
+#define fnt__l_raster_count 220
+
+ i = lnf[fnt__l_first_character+4]-
+ lnf[fnt__l_first_character]+1;
+ ras_beg = lnf_chardir + 4*i + 4 + 48;
+ lnf[fnt__l_infile_locators] = i;
+ lnf[fnt__l_character_definitions] = i;
+ lnf[fnt__l_raster_count] = i;
+ lnf[ras_beg-52] = lnf[ras_beg-51] = lnf[ras_beg-50] =
+ lnf[ras_beg-49] = 0;
+
+/* Set the font file id. This id is a 31-byte string. We make it depend on
+the lnfno number, and on whether this is a left or right font.
+Specifically, the first seven bytes are U0000nn where nn is lnfno possibly
+with a leading zero. The character set part of the ID is 1O if using_r,
+otherwise the default from good_opening. */
+
+#define fnt__t_font_file_id 20
+
+ lnf[fnt__t_font_file_id+5] = lnfno/10+'0';
+ lnf[fnt__t_font_file_id+6] = lnfno % 10 + '0';
+ if (using_r) lnf[fnt__t_font_file_id+20] = 'O';
+
+/* We now have to set some fields in the font format that are pointers to
+where other fields begin. These pointers depend on the number of glyphs we
+are placing in the font file. */
+
+#define fnt__a_subset_tables 128
+
+ lcy.l = lnf_chardir+4*i;
+ copy_to_lnf(fnt__a_subset_tables);
+
+ lcy.l = lcy.l+4;
+ copy_to_lnf(fnt__a_subset_tables+8);
+ copy_to_lnf(fnt__a_subset_tables+16);
+
+ lcy.l = lcy.l+48;
+ copy_to_lnf(fnt__a_subset_tables+24);
+
+#define fnt__a_char_definitions 160
+
+ copy_to_lnf(fnt__a_char_definitions);
+
+/* Set up the string pool region of the font load (48 bytes). The only
+tricky part here is the character set designator at the very beginning of
+the pool. Left fonts use one designator, right fonts another. [[More
+explanation is needed.]] */
+
+ pool_beg = ras_beg-48;
+
+ lnf[pool_beg] = '0';
+ lnf[pool_beg+1] = using_r ? '<':'B';
+ lnf[pool_beg+2] = '\011'; /* ascii tab */
+ lnf[pool_beg+3] = 'Z';
+ lnf[pool_beg+4] = 'Z';
+ lnf[pool_beg+5] = 'Z';
+ lnf[pool_beg+6] = 'Z';
+
+/* For reasons that are beyond my comprehension, the Common Font File
+Format requires us to copy the first seven characters of the font file id
+into one piece of the string pool, and the first sixteen into another
+place. */
+
+ for (i=1; i<8; i++)
+ lnf[pool_beg+7+i-1] = lnf[fnt__t_font_file_id+i-1];
+ for (i=1; i<17; i++)
+ lnf[pool_beg+7+7+i-1] = ' ';
+ for (i=1; i<17; i++)
+ lnf[pool_beg+7+7+16+i-1] = lnf[fnt__t_font_file_id+i-1];
+ lnf[pool_beg+46] = lnf[pool_beg+47] = 0;
+
+/* Now make string descriptors point into the string pool. */
+
+#define fnt__a_char_set 260
+#define fnt__a_family_id 268
+#define fnt__a_family_name 276
+#define fnt__a_font_id 284
+
+ lcy.l = pool_beg;
+ copy_to_lnf(fnt__a_char_set);
+ lcy.l = pool_beg+7;
+ copy_to_lnf(fnt__a_family_id);
+ lcy.l = pool_beg+7+7;
+ copy_to_lnf(fnt__a_family_name);
+ lcy.l = pool_beg+7+7+16;
+ copy_to_lnf(fnt__a_font_id);
+
+/* Mercifully, all remaining font file slots that we can fill now are
+already good as received from good_opening. */
+
+}
diff --git a/dviware/ln03/rose/ffc.pas b/dviware/ln03/rose/ffc.pas
new file mode 100644
index 0000000000..e33f4bd03d
--- /dev/null
+++ b/dviware/ln03/rose/ffc.pas
@@ -0,0 +1,277 @@
+{4:}PROGRAM FFC(INPUT,OUTPUT,LN03FILE,PXLFILE,OUTFILE);LABEL{5:}
+9998,9999;{:5}TYPE{6:}EIGHTBITS=0..255;
+BYTEBLOCK=PACKED ARRAY[0..511]OF EIGHTBITS;
+BYTEFILE=PACKED FILE OF BYTEBLOCK;{:6}VAR{7:}INLINE:VARYING[513]OF CHAR;
+ILP,ISTART,ILLEN:INTEGER;VERB:INTEGER;{:7}{11:}LN03FILE:BYTEFILE;
+LN03COUNT,LN03LEN,I:INTEGER;
+LBUF:PACKED RECORD CASE BOOLEAN OF FALSE:(C:PACKED ARRAY[0..((512*512)-1
+)]OF CHAR);TRUE:(B:PACKED ARRAY[0..511]OF BYTEBLOCK);END;{:11}{15:}
+PXLFILE:BYTEFILE;PXLCOUNT,PXLLEN:INTEGER;
+PBUF:PACKED RECORD CASE BOOLEAN OF FALSE:(C:PACKED ARRAY[0..((512*512)-1
+)]OF CHAR);TRUE:(B:PACKED ARRAY[0..511]OF BYTEBLOCK);END;{:15}{22:}
+DEFSTART,RASSTART:INTEGER;J,K,L,M,N:INTEGER;
+VISIBLEBYTE:PACKED ARRAY[1..8]OF CHAR;{:22}{31:}
+CHARXDEFOFFS,STRINGPOOLSI,STRINGXPOOLO:INTEGER;
+PSIZE,LSIZE,MSIZE:INTEGER;FIRSTCHAR,LASTCHAR,NUMCHARS:INTEGER;
+ALLBLANK:BOOLEAN;ZCHAR,TFMWIDTH,XOFFSET,YOFFSET:INTEGER;DSIZE,MAG:REAL;
+{:31}{39:}FONTIDSTRING:PACKED ARRAY[1..31]OF CHAR;{:39}{44:}
+POINTS:INTEGER;{:44}{47:}OUTFILE:BYTEFILE;{:47}{8:}
+FUNCTION COMMANDVERB:INTEGER;VAR I:INTEGER;BEGIN{9:}
+WHILE(ILP<=ILLEN)AND(INLINE[ILP]=' ')DO ILP:=ILP+1;ISTART:=ILP;
+WHILE(ILP<=ILLEN)AND(INLINE[ILP]<>' ')DO ILP:=ILP+1;
+FOR I:=ISTART TO ILP-1 DO BEGIN IF(INLINE[I]>='A')AND(INLINE[I]<='Z')
+THEN INLINE[I]:=CHR(ORD(INLINE[I])+ORD('a')-ORD('A'))END;{:9}
+IF INDEX(SUBSTR(INLINE,ISTART,ILP-ISTART),'rln03')=1 THEN COMMANDVERB:=1
+ELSE IF INDEX(SUBSTR(INLINE,ISTART,ILP-ISTART),'tln03long')=1 THEN
+COMMANDVERB:=3 ELSE IF INDEX(SUBSTR(INLINE,ISTART,ILP-ISTART),
+'tln03word')=1 THEN COMMANDVERB:=4 ELSE IF INDEX(SUBSTR(INLINE,ISTART,
+ILP-ISTART),'tln03')=1 THEN COMMANDVERB:=2 ELSE IF INDEX(SUBSTR(INLINE,
+ISTART,ILP-ISTART),'rpxl')=1 THEN COMMANDVERB:=5 ELSE IF INDEX(SUBSTR(
+INLINE,ISTART,ILP-ISTART),'tpxllong')=1 THEN COMMANDVERB:=7 ELSE IF
+INDEX(SUBSTR(INLINE,ISTART,ILP-ISTART),'tpxlword')=1 THEN COMMANDVERB:=8
+ELSE IF INDEX(SUBSTR(INLINE,ISTART,ILP-ISTART),'tpxl')=1 THEN
+COMMANDVERB:=6 ELSE IF INDEX(SUBSTR(INLINE,ISTART,ILP-ISTART),'toln03x')
+=1 THEN COMMANDVERB:=11 ELSE IF INDEX(SUBSTR(INLINE,ISTART,ILP-ISTART),
+'toln03')=1 THEN COMMANDVERB:=9 ELSE IF INDEX(SUBSTR(INLINE,ISTART,ILP-
+ISTART),'wln03')=1 THEN COMMANDVERB:=10 ELSE IF INDEX(SUBSTR(INLINE,
+ISTART,ILP-ISTART),'exit')=1 THEN COMMANDVERB:=99 ELSE COMMANDVERB:=0
+END;{:8}{19:}FUNCTION GETFIXNUM:INTEGER;LABEL 1;VAR X,X1:INTEGER;
+NEGATIVE:BOOLEAN;BEGIN X1:=0;NEGATIVE:=FALSE;
+WHILE(ILP<=ILLEN)AND(INLINE[ILP]=' ')DO ILP:=ILP+1;
+IF ILP>ILLEN THEN GOTO 1;IF INLINE[ILP]='-'THEN BEGIN NEGATIVE:=TRUE;
+ILP:=ILP+1 END;
+IF(ILP>ILLEN)OR(INLINE[ILP]<'0')OR(INLINE[ILP]>'9')THEN GOTO 1;X1:=0;
+WHILE(INLINE[ILP]>='0')AND(INLINE[ILP]<='9')DO BEGIN X1:=10*X1+ORD(
+INLINE[ILP])-ORD('0');ILP:=ILP+1;IF ILP>ILLEN THEN GOTO 1;END;1:X:=X1;
+IF NEGATIVE THEN X:=-X;GETFIXNUM:=X END;{:19}{25:}
+PROCEDURE BINREP(V:INTEGER);VAR CNT,REM,QUO:INTEGER;
+BEGIN VISIBLEBYTE:='........';QUO:=V;
+FOR CNT:=1 TO 8 DO BEGIN REM:=QUO MOD 2;QUO:=QUO DIV 2;
+IF REM<>0 THEN VISIBLEBYTE[CNT]:='B'END;END;{:25}{27:}
+PROCEDURE REVBINREP(V:INTEGER);VAR CNT,REM,QUO:INTEGER;
+BEGIN VISIBLEBYTE:='........';QUO:=V;
+FOR CNT:=1 TO 8 DO BEGIN REM:=QUO MOD 2;QUO:=QUO DIV 2;
+IF REM<>0 THEN VISIBLEBYTE[9-CNT]:='B'END;END;{:27}{34:}
+FUNCTION SIGNEDPXLWOR(INDEX:INTEGER):INTEGER;
+BEGIN IF ORD(PBUF.C[INDEX])>=128 THEN SIGNEDPXLWOR:=(ORD(PBUF.C[(INDEX)
++1])+256*ORD(PBUF.C[INDEX]))-65536 ELSE SIGNEDPXLWOR:=(ORD(PBUF.C[(INDEX
+)+1])+256*ORD(PBUF.C[INDEX]))END;
+PROCEDURE SETLN03LONG(INDEX,VALUE:INTEGER);VAR NEGATIVE:BOOLEAN;
+A,B,C,D,CARRY:INTEGER;
+BEGIN IF VALUE<0 THEN NEGATIVE:=TRUE ELSE NEGATIVE:=FALSE;
+IF NEGATIVE THEN VALUE:=-VALUE;A:=VALUE MOD 256;
+B:=(VALUE DIV 256)MOD 256;C:=(VALUE DIV(256*256))MOD 256;
+D:=VALUE DIV(256*256*256);IF NEGATIVE THEN BEGIN CARRY:=0;A:=256-A;
+IF A=256 THEN BEGIN A:=0;CARRY:=1 END;B:=255+CARRY-B;
+IF B=256 THEN B:=0 ELSE CARRY:=0;C:=255+CARRY-C;
+IF C=256 THEN C:=0 ELSE CARRY:=0;D:=255+CARRY-D;IF D=256 THEN D:=0 END;
+LBUF.C[INDEX]:=CHR(A);LBUF.C[INDEX+1]:=CHR(B);LBUF.C[INDEX+2]:=CHR(C);
+LBUF.C[INDEX+3]:=CHR(D)END;{:34}{36:}FUNCTION REVERSEBYTE(U:CHAR):CHAR;
+VAR CNT,RV:INTEGER;BEGIN BINREP(ORD(U));RV:=0;
+FOR CNT:=1 TO 8 DO IF VISIBLEBYTE[CNT]='B'THEN RV:=1+2*RV ELSE RV:=2*RV;
+REVERSEBYTE:=CHR(RV)END;{:36}
+BEGIN WRITELN('Font File Converter, Version 3');WRITELN;{10:}
+9998:WRITELN;WRITE('FFC>');READLN(INLINE);
+IF LENGTH(INLINE)=513 THEN BEGIN WRITELN('Command line too long');
+GOTO 9998 END;INLINE:=INLINE+' ';ILP:=1;ILLEN:=LENGTH(INLINE);
+VERB:=COMMANDVERB;IF VERB=0 THEN BEGIN WRITELN('No such command');
+GOTO 9998 END ELSE{12:}
+IF VERB=1 THEN BEGIN WHILE(ILP<=ILLEN)AND(INLINE[ILP]=' ')DO ILP:=ILP+1;
+ISTART:=ILP;WHILE(ILP<=ILLEN)AND(INLINE[ILP]<>' ')DO ILP:=ILP+1;
+OPEN(LN03FILE,SUBSTR(INLINE,ISTART,ILP-ISTART),readonly,error:=continue)
+;IF STATUS(LN03FILE)>0 THEN BEGIN WRITELN('Couldn''t open file');
+GOTO 9998 END;RESET(LN03FILE);{13:}LN03COUNT:=0;
+WHILE(NOT EOF(LN03FILE))AND(LN03COUNT<512)DO BEGIN READ(LN03FILE,LBUF.B[
+LN03COUNT]);LN03COUNT:=LN03COUNT+1;END;LN03LEN:=LN03COUNT*512;
+CLOSE(LN03FILE);{14:}IF LN03LEN<16 THEN WRITELN('LN03 file too short');
+IF(LBUF.C[4]<>'F')OR(LBUF.C[5]<>'O')OR(LBUF.C[6]<>'N')OR(LBUF.C[7]<>'T')
+THEN WRITELN('Second longword not FONT'){:14}{:13}END{:12}{16:}
+ELSE IF VERB=5 THEN BEGIN WHILE(ILP<=ILLEN)AND(INLINE[ILP]=' ')DO ILP:=
+ILP+1;ISTART:=ILP;WHILE(ILP<=ILLEN)AND(INLINE[ILP]<>' ')DO ILP:=ILP+1;
+OPEN(PXLFILE,SUBSTR(INLINE,ISTART,ILP-ISTART),readonly,error:=continue);
+IF STATUS(PXLFILE)>0 THEN BEGIN WRITELN('Couldn''t open file');
+GOTO 9998 END;RESET(PXLFILE);{17:}PXLCOUNT:=0;
+WHILE(NOT EOF(PXLFILE))AND(PXLCOUNT<512)DO BEGIN READ(PXLFILE,PBUF.B[
+PXLCOUNT]);PXLCOUNT:=PXLCOUNT+1;END;PXLLEN:=PXLCOUNT*512;CLOSE(PXLFILE);
+{18:}
+IF NOT(PXLLEN MOD 4=0)THEN WRITELN('PXL file length not multiple of 4');
+IF(ORD(PBUF.C[(0)+3])+(256*(ORD(PBUF.C[(0)+2])+256*(ORD(PBUF.C[(0)+1])
++256*ORD(PBUF.C[0])))))<>1001 THEN WRITELN('Initial PXL format id wrong'
+);I:=PXLLEN-4;
+WHILE(I>=PXLLEN-512)DO BEGIN IF(ORD(PBUF.C[(I)+3])+(256*(ORD(PBUF.C[(I)
++2])+256*(ORD(PBUF.C[(I)+1])+256*ORD(PBUF.C[I])))))=1001 THEN BEGIN
+PXLLEN:=I+4;I:=-1 END ELSE I:=I-4 END;
+IF PXLLEN<16 THEN WRITELN('PXL file too short');
+IF(ORD(PBUF.C[(PXLLEN-4)+3])+(256*(ORD(PBUF.C[(PXLLEN-4)+2])+256*(ORD(
+PBUF.C[(PXLLEN-4)+1])+256*ORD(PBUF.C[PXLLEN-4])))))<>1001 THEN WRITELN(
+'Final PXL format id wrong');{:18}{:17}END{:16}{21:}
+ELSE IF VERB=2 THEN BEGIN WHILE(ILP<=ILLEN)AND(INLINE[ILP]=' ')DO ILP:=
+ILP+1;
+IF ILP>ILLEN THEN BEGIN WRITELN('You have to say what character to type'
+);GOTO 9998 END;
+IF(INLINE[ILP]='''')AND(ILP<ILLEN)THEN I:=ORD(INLINE[ILP+1])ELSE I:=
+GETFIXNUM;
+IF(I<(ORD(LBUF.C[164])+(256*(ORD(LBUF.C[(164)+1])+256*(ORD(LBUF.C[(164)
++2])+256*ORD(LBUF.C[(164)+3]))))))OR(I>(ORD(LBUF.C[168])+(256*(ORD(LBUF.
+C[(168)+1])+256*(ORD(LBUF.C[(168)+2])+256*ORD(LBUF.C[(168)+3]))))))THEN
+BEGIN WRITELN('No such character');GOTO 9998 END;
+I:=4*(I-(ORD(LBUF.C[164])+(256*(ORD(LBUF.C[(164)+1])+256*(ORD(LBUF.C[(
+164)+2])+256*ORD(LBUF.C[(164)+3]))))))+(ORD(LBUF.C[120])+(256*(ORD(LBUF.
+C[(120)+1])+256*(ORD(LBUF.C[(120)+2])+256*ORD(LBUF.C[(120)+3])))));{23:}
+IF LBUF.C[I+3]>=CHR(128)THEN BEGIN WRITELN(
+'Indirect locators not supported');GOTO 9998 END;
+DEFSTART:=ORD(LBUF.C[I])+256*(ORD(LBUF.C[I+1])+256*ORD(LBUF.C[(I+1)+1]))
+;IF DEFSTART=0 THEN BEGIN WRITELN('Locator is zero');GOTO 9998 END;
+RASSTART:=DEFSTART+(ORD(LBUF.C[200])+(256*(ORD(LBUF.C[(200)+1])+256*(ORD
+(LBUF.C[(200)+2])+256*ORD(LBUF.C[(200)+3])))));
+WRITELN(' ma = ',RASSTART:1);
+IF RASSTART>((512*512)-1)THEN BEGIN WRITELN(
+'Character outside piece of file read');GOTO 9998 END{:23};{24:}
+WRITELN('width ',(((ORD(LBUF.C[DEFSTART+4])+(256*(ORD(LBUF.C[(DEFSTART+4
+)+1])+256*(ORD(LBUF.C[(DEFSTART+4)+2])+256*ORD(LBUF.C[(DEFSTART+4)+3])))
+)))/24.0):5:1,' pixels');
+IF ORD(LBUF.C[RASSTART+1])<>129 THEN BEGIN WRITELN(
+'Run length encoding not supported');GOTO 9998 END;
+IF ODD(ORD(LBUF.C[RASSTART]))THEN BEGIN WRITELN('landscape');
+I:=(ORD(LBUF.C[RASSTART+4])+256*ORD(LBUF.C[(RASSTART+4)+1]));
+J:=(ORD(LBUF.C[RASSTART+6])+256*ORD(LBUF.C[(RASSTART+6)+1]))END ELSE
+BEGIN WRITELN('portrait');
+I:=(ORD(LBUF.C[RASSTART+6])+256*ORD(LBUF.C[(RASSTART+6)+1]));
+J:=(ORD(LBUF.C[RASSTART+4])+256*ORD(LBUF.C[(RASSTART+4)+1]))END;
+WRITELN(I:1,' rows ',J:1,' columns');K:=I DIV 8;IF I<>8*K THEN K:=K+1;
+FOR L:=0 TO J-1 DO BEGIN FOR M:=0 TO K-1 DO BEGIN BINREP(ORD(LBUF.C[
+RASSTART+8+K*L+M]));WRITE(VISIBLEBYTE)END;WRITELN END{:24}END{:21}{26:}
+ELSE IF VERB=6 THEN BEGIN WHILE(ILP<=ILLEN)AND(INLINE[ILP]=' ')DO ILP:=
+ILP+1;
+IF ILP>ILLEN THEN BEGIN WRITELN('You have to say what character to type'
+);GOTO 9998 END;
+IF(INLINE[ILP]='''')AND(ILP<ILLEN)THEN I:=ORD(INLINE[ILP+1])ELSE I:=
+GETFIXNUM;IF(I<0)OR(I>127)THEN BEGIN WRITELN('No such character');
+GOTO 9998 END;DEFSTART:=PXLLEN-4*517+16*I;
+RASSTART:=4*(ORD(PBUF.C[(DEFSTART+8)+3])+(256*(ORD(PBUF.C[(DEFSTART+8)+2
+])+256*(ORD(PBUF.C[(DEFSTART+8)+1])+256*ORD(PBUF.C[DEFSTART+8])))));
+WRITELN(' rasters at ',RASSTART:1);
+IF RASSTART>PXLLEN THEN BEGIN WRITELN('Rasters outside file');
+GOTO 9998 END;I:=(ORD(PBUF.C[(DEFSTART)+1])+256*ORD(PBUF.C[DEFSTART]));
+J:=(ORD(PBUF.C[(DEFSTART+2)+1])+256*ORD(PBUF.C[DEFSTART+2]));
+WRITELN(I:1,' columns ',J:1,' rows');K:=I DIV 32;IF I<>32*K THEN K:=K+1;
+FOR L:=0 TO J-1 DO BEGIN FOR M:=0 TO K-1 DO BEGIN REVBINREP(ORD(PBUF.C[
+RASSTART+4*K*L+4*M]));WRITE(VISIBLEBYTE);
+REVBINREP(ORD(PBUF.C[RASSTART+4*K*L+4*M+1]));WRITE(VISIBLEBYTE);
+REVBINREP(ORD(PBUF.C[RASSTART+4*K*L+4*M+2]));WRITE(VISIBLEBYTE);
+REVBINREP(ORD(PBUF.C[RASSTART+4*K*L+4*M+3]));WRITE(VISIBLEBYTE)END;
+WRITELN END END{:26}{28:}ELSE IF VERB=3 THEN BEGIN I:=GETFIXNUM;
+IF(I>=0)AND(I<LN03LEN)THEN WRITELN((ORD(LBUF.C[I])+(256*(ORD(LBUF.C[(I)
++1])+256*(ORD(LBUF.C[(I)+2])+256*ORD(LBUF.C[(I)+3]))))):1)ELSE WRITELN(
+'Location not in file')END ELSE IF VERB=4 THEN BEGIN I:=GETFIXNUM;
+IF(I>=0)AND(I<LN03LEN)THEN WRITELN((ORD(LBUF.C[I])+256*ORD(LBUF.C[(I)+1]
+)))ELSE WRITELN('Location not in file')END ELSE IF VERB=7 THEN BEGIN I:=
+GETFIXNUM;
+IF(I>=0)AND(I<PXLLEN)THEN WRITELN((ORD(PBUF.C[(I)+3])+(256*(ORD(PBUF.C[(
+I)+2])+256*(ORD(PBUF.C[(I)+1])+256*ORD(PBUF.C[I]))))):1)ELSE WRITELN(
+'Location not in file')END ELSE IF VERB=8 THEN BEGIN I:=GETFIXNUM;
+IF(I>=0)AND(I<PXLLEN)THEN WRITELN((ORD(PBUF.C[(I)+1])+256*ORD(PBUF.C[I])
+))ELSE WRITELN('Location not in file')END{:28}{30:}
+ELSE IF(VERB=9)OR(VERB=11)THEN BEGIN FOR I:=0 TO((512*512)-1)DO LBUF.C[I
+]:=CHR(0);IF VERB=9 THEN BEGIN FIRSTCHAR:=33;
+LASTCHAR:=126 END ELSE BEGIN FIRSTCHAR:=0;LASTCHAR:=127 END;
+NUMCHARS:=LASTCHAR-FIRSTCHAR+1;{32:}STRINGPOOLSI:=48;
+STRINGXPOOLO:=480+4*NUMCHARS+4;CHARXDEFOFFS:=STRINGXPOOLO+STRINGPOOLSI;
+LN03LEN:=CHARXDEFOFFS;PSIZE:=0;LSIZE:=0;MSIZE:=0;
+FOR ZCHAR:=FIRSTCHAR TO LASTCHAR DO BEGIN{33:}
+LBUF.C[LN03LEN+3]:=CHR(128);DEFSTART:=PXLLEN-4*517+16*ZCHAR;
+RASSTART:=4*(ORD(PBUF.C[(DEFSTART+8)+3])+(256*(ORD(PBUF.C[(DEFSTART+8)+2
+])+256*(ORD(PBUF.C[(DEFSTART+8)+1])+256*ORD(PBUF.C[DEFSTART+8])))));
+IF RASSTART>PXLLEN THEN BEGIN WRITELN('Rasters outside file for ',ZCHAR:
+1);GOTO 9998 END;
+TFMWIDTH:=(ORD(PBUF.C[(DEFSTART+12)+3])+(256*(ORD(PBUF.C[(DEFSTART+12)+2
+])+256*(ORD(PBUF.C[(DEFSTART+12)+1])+256*ORD(PBUF.C[DEFSTART+12])))));
+DSIZE:=(ORD(PBUF.C[(PXLLEN-12)+3])+(256*(ORD(PBUF.C[(PXLLEN-12)+2])+256*
+(ORD(PBUF.C[(PXLLEN-12)+1])+256*ORD(PBUF.C[PXLLEN-12])))));
+MAG:=(ORD(PBUF.C[(PXLLEN-16)+3])+(256*(ORD(PBUF.C[(PXLLEN-16)+2])+256*(
+ORD(PBUF.C[(PXLLEN-16)+1])+256*ORD(PBUF.C[PXLLEN-16])))));
+DSIZE:=(DSIZE/(1048576))*(MAG/1500.0);XOFFSET:=SIGNEDPXLWOR(DEFSTART+4);
+YOFFSET:=SIGNEDPXLWOR(DEFSTART+6);
+SETLN03LONG(LN03LEN+4,ROUND((DSIZE*7200.0*TFMWIDTH)/((1048576)*72.27)));
+SETLN03LONG(LN03LEN+8,-24*XOFFSET);SETLN03LONG(LN03LEN+12,-24*YOFFSET);
+{:33}{35:}LBUF.C[LN03LEN+17]:=CHR(129);
+I:=(ORD(PBUF.C[(DEFSTART)+1])+256*ORD(PBUF.C[DEFSTART]));
+J:=(ORD(PBUF.C[(DEFSTART+2)+1])+256*ORD(PBUF.C[DEFSTART+2]));
+ALLBLANK:=(I=0)AND(J=0);IF ALLBLANK THEN BEGIN I:=1;J:=1 END;
+LBUF.C[LN03LEN+20]:=CHR(J MOD 256);LBUF.C[LN03LEN+21]:=CHR(J DIV 256);
+LBUF.C[LN03LEN+22]:=CHR(I MOD 256);LBUF.C[LN03LEN+23]:=CHR(I DIV 256);
+K:=I DIV 32;IF I<>32*K THEN K:=K+1;N:=I DIV 8;IF I<>8*N THEN N:=N+1;
+IF NOT ALLBLANK THEN FOR L:=0 TO J-1 DO FOR M:=0 TO N-1 DO LBUF.C[
+LN03LEN+24+N*L+M]:=REVERSEBYTE(PBUF.C[RASSTART+4*K*L+M]);
+SETLN03LONG(480+4*(ZCHAR-FIRSTCHAR),LN03LEN);LN03LEN:=LN03LEN+24+J*N;
+IF ODD(LN03LEN)THEN LN03LEN:=LN03LEN+1;PSIZE:=PSIZE+J*N;K:=J DIV 8;
+IF J<>8*K THEN K:=K+1;LSIZE:=LSIZE+I*K;
+IF I*K>J*N THEN MSIZE:=MSIZE+I*K ELSE MSIZE:=MSIZE+J*N;{:35}END;{:32};
+{37:}IF LN03LEN MOD 4<>0 THEN LN03LEN:=LN03LEN+(4-(LN03LEN MOD 4));
+LN03LEN:=LN03LEN+8;SETLN03LONG(0,LN03LEN);
+SETLN03LONG(LN03LEN-8,LN03LEN);LBUF.C[4]:='F';LBUF.C[LN03LEN-4]:='F';
+LBUF.C[5]:='O';LBUF.C[LN03LEN-3]:='O';LBUF.C[6]:='N';
+LBUF.C[LN03LEN-2]:='N';LBUF.C[7]:='T';LBUF.C[LN03LEN-1]:='T';{:37}{38:}
+LBUF.C[8]:=CHR(1);LBUF.C[12]:=CHR(31);LBUF.C[16]:=CHR(20);
+FONTIDSTRING:='U000000002SK00GG0001UZZZZ02F000';
+FOR I:=1 TO 31 DO LBUF.C[20+I-1]:=FONTIDSTRING[I];
+LBUF.C[88]:=CHR(1973 MOD 256);LBUF.C[89]:=CHR(1973 DIV 256);
+LBUF.C[90]:=CHR(9);LBUF.C[92]:=CHR(11);LBUF.C[94]:=CHR(14);{:38}{40:}
+SETLN03LONG(100,104);SETLN03LONG(104,252);SETLN03LONG(108,124);
+SETLN03LONG(112,356);SETLN03LONG(116,4*NUMCHARS);SETLN03LONG(120,480);
+SETLN03LONG(124,4);SETLN03LONG(128,480+4*NUMCHARS);
+SETLN03LONG(136,480+4*NUMCHARS+4);SETLN03LONG(140,STRINGPOOLSI);
+SETLN03LONG(144,480+4*NUMCHARS+4);
+SETLN03LONG(152,480+4*NUMCHARS+4+STRINGPOOLSI);
+SETLN03LONG(156,LN03LEN-8-CHARXDEFOFFS);SETLN03LONG(160,CHARXDEFOFFS);
+{:40}{41:}SETLN03LONG(164,FIRSTCHAR);SETLN03LONG(168,LASTCHAR);
+SETLN03LONG(192,32);SETLN03LONG(196,168);SETLN03LONG(200,16);
+SETLN03LONG(204,NUMCHARS);SETLN03LONG(212,NUMCHARS);
+SETLN03LONG(220,NUMCHARS);SETLN03LONG(228,PSIZE);SETLN03LONG(232,LSIZE);
+SETLN03LONG(236,MSIZE);{:41}{42:}SETLN03LONG(252,2);SETLN03LONG(256,7);
+SETLN03LONG(260,STRINGXPOOLO);LBUF.C[STRINGXPOOLO]:='0';
+LBUF.C[STRINGXPOOLO+1]:='B';LBUF.C[STRINGXPOOLO+2]:=CHR(9);
+LBUF.C[STRINGXPOOLO+3]:='Z';LBUF.C[STRINGXPOOLO+4]:='Z';
+LBUF.C[STRINGXPOOLO+5]:='Z';LBUF.C[STRINGXPOOLO+6]:='Z';
+SETLN03LONG(264,7);SETLN03LONG(268,STRINGXPOOLO+7);
+FOR I:=1 TO 7 DO LBUF.C[STRINGXPOOLO+7+I-1]:=FONTIDSTRING[I];
+SETLN03LONG(272,16);SETLN03LONG(276,STRINGXPOOLO+14);
+FOR I:=1 TO 16 DO LBUF.C[STRINGXPOOLO+14+I-1]:=' ';SETLN03LONG(280,16);
+SETLN03LONG(284,STRINGXPOOLO+30);
+FOR I:=1 TO 16 DO LBUF.C[STRINGXPOOLO+30+I-1]:=FONTIDSTRING[I];{:42}
+{43:}
+I:=(ORD(PBUF.C[(PXLLEN-12)+3])+(256*(ORD(PBUF.C[(PXLLEN-12)+2])+256*(ORD
+(PBUF.C[(PXLLEN-12)+1])+256*ORD(PBUF.C[PXLLEN-12])))));
+POINTS:=I DIV(1048576);K:=(10000*I MOD(1048576))DIV(1048576);
+LBUF.C[304]:=CHR(POINTS MOD 256);LBUF.C[305]:=CHR(POINTS DIV 256);
+LBUF.C[306]:=CHR(K MOD 256);LBUF.C[307]:=CHR(K DIV 256);
+IF K>4999 THEN POINTS:=POINTS+1;SETLN03LONG(308,50*POINTS);{:43}{45:}
+LBUF.C[314]:=CHR(24);LBUF.C[316]:=CHR(16);LBUF.C[320]:=CHR(16);
+LBUF.C[324]:=CHR(1);LBUF.C[326]:=CHR(1);LBUF.C[328]:=CHR(1);
+LBUF.C[330]:=CHR(1);LBUF.C[334]:=CHR(1);{:45}{46:}
+SETLN03LONG(360,12*POINTS);SETLN03LONG(364,8*POINTS);
+SETLN03LONG(368,-25*POINTS);SETLN03LONG(372,8*POINTS);
+SETLN03LONG(376,-60*POINTS);SETLN03LONG(380,8*POINTS);
+LBUF.C[386]:=CHR(1);LBUF.C[390]:=CHR(POINTS*12 MOD 256);
+LBUF.C[391]:=CHR(POINTS*12 DIV 256);SETLN03LONG(392,-36*POINTS);
+SETLN03LONG(400,16*POINTS);SETLN03LONG(408,24*POINTS);
+SETLN03LONG(412,20*POINTS);SETLN03LONG(416,80*POINTS);
+SETLN03LONG(420,25*POINTS);SETLN03LONG(424,100*POINTS);
+SETLN03LONG(428,50*POINTS);SETLN03LONG(432,10*POINTS);
+SETLN03LONG(436,35*POINTS);SETLN03LONG(440,-64*POINTS);
+SETLN03LONG(444,-50*POINTS);SETLN03LONG(448,-35*POINTS);
+SETLN03LONG(452,100*POINTS);SETLN03LONG(456,-65*POINTS);
+SETLN03LONG(460,35*POINTS);SETLN03LONG(464,65*POINTS);
+SETLN03LONG(468,35*POINTS);SETLN03LONG(472,10*POINTS);
+SETLN03LONG(476,10*POINTS);{:46}END{:30}{48:}
+ELSE IF VERB=10 THEN BEGIN WHILE(ILP<=ILLEN)AND(INLINE[ILP]=' ')DO ILP:=
+ILP+1;
+IF ILP>ILLEN THEN BEGIN WRITELN('You must specify a file to write into')
+;GOTO 9998 END;ISTART:=ILP;
+WHILE(ILP<=ILLEN)AND(INLINE[ILP]<>' ')DO ILP:=ILP+1;
+OPEN(OUTFILE,SUBSTR(INLINE,ISTART,ILP-ISTART),error:=continue);
+IF STATUS(OUTFILE)<>0 THEN BEGIN WRITELN('couldn''t open',SUBSTR(INLINE,
+ISTART,ILP-ISTART));GOTO 9998 END;REWRITE(OUTFILE);I:=LN03LEN DIV 512;
+IF LN03LEN<>I*512 THEN I:=I+1;
+FOR J:=0 TO I-1 DO WRITE(OUTFILE,LBUF.B[J]);CLOSE(OUTFILE)END{:48}{49:}
+ELSE IF VERB=99 THEN GOTO 9999{:49};GOTO 9998;9999:{:10}END.{:4}
diff --git a/dviware/ln03/rose/ffc.web b/dviware/ln03/rose/ffc.web
new file mode 100644
index 0000000000..eaa79d69a9
--- /dev/null
+++ b/dviware/ln03/rose/ffc.web
@@ -0,0 +1,991 @@
+% This program interconverts files between TeX and LN03 font file formats.
+
+% Copyright (c) 1984, 1985 by Digital Equipment Corporation.
+% UNDER DEVELOPMENT
+% Version 0.0, early December 1984, initial functionality
+% Version 0.0a, fixes by Scott Robinson to make input more efficient
+% Version 0.1, late December, fixed bug: magnification field is no
+% longer ignored in PXL to LN03 conversion.
+% Version 2, April-June 1985, a lot of random fixes.
+% Version 3, January 1986, tln03 prints width.
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=amr9
+
+\def\title{Font File Converter}
+\def\contentspagenumber{1}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The Font File Converter}
+ \vskip 15pt
+ \centerline{(Version 3, January 1986)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ Copyright 1984, 1985, 1986 by Digital Equipment Corporation.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction. The purpose of the Font File Converter (FFC) program is to
+convert font files from the \TeX\ \.{PXL} format to the DEC Common Font
+File Format. The program takes interactive commands from the terminal.
+Right now, its conversion capability is quite limited, and the files FFC
+produces are essentially usable only with the accompanying LN03 driver for
+\TeX.
+
+A |banner| string is defined to identify the version:
+
+@d banner=='Font File Converter, Version 3'
+
+@ FFC is written in the Web language, but the underlying Pascal is
+\hbox{VAX--11} Pascal, and no effort has been made to make anything
+portable@^portability@>. In particular, it is assumed that Pascal strings
+are ASCII strings.
+
+We do, however, use the ``standard'' Web name for the default in a
+|case| statement:
+
+@f othercases == else
+@d othercases == otherwise
+
+@ Detailed instructions for using FFC are given in a separate document. The
+LN03 and \.{PXL} file formats are also described separately.
+
+The LN03 accepts font files in the DEC Common Font File Format. However, it
+only reads certain fields of that format. FFC fills enough of these fields
+to make the font files it generates acceptable to the LN03, when these
+files are first massaged by the accompanying \TeX\ driver software. It
+cannot be assumed that FFC fills all fields in the Common Font File Format
+correctly. Neither can it be assumed that the files it generates are
+acceptable to other software or devices that use the DEC Common Font File
+Format.
+
+@ The overall structure of FFC is given below.
+
+@p
+program FFC (input, output, ln03_file, pxl_file, outfile);
+ label @<Labels in the outer block@>@/
+ type @<Types in the outer block@>@/
+ var @<Globals in the outer block@>@/
+ @<Major procedures@>@/
+begin
+ writeln(banner); writeln;@/
+ @<The main program@>@/
+end.
+
+@ There are two global labels, |final_end| for use when the program finishes,
+and |cmd_prompt| for use when a command comes to an end:
+
+@d final_end = 9999
+@d cmd_prompt = 9998
+
+@<Labels ...@>=
+ cmd_prompt, final_end;
+
+@ We define a file type |byte_file| for files consisting of 512-byte
+blocks. This definition was introduced by Scott Robinson for efficiency
+reasons. It is unclear at this point how this Pascal definition restricts
+our ability to work with font files of the various types that can exist
+in a Vax/VMS file system.
+
+@<Types...@>=
+@!eight_bits=0..255;
+@!byte_block=packed array [0..511] of eight_bits;
+@!byte_file=packed file of byte_block; {files that contain binary data}
+
+@* Command input. The user's command lines are read into a buffer called
+|inline|. The variables |ilp| and |istart| are used to point into it,
+while |illen| keeps track of its length. The variable |verb| holds an
+integer code representing the command verb.
+
+@d the_char == inline[ilp]
+@d the_substr == substr(inline,istart,ilp-istart)
+
+@<Globals...@>=
+@!inline: varying[513] of char;
+@!ilp,istart,illen :integer;
+@!verb :integer;
+
+@ Each command consists of a verb, a space, and arguments. The command
+handling procedure itself parses the arguments from |inline|. The verbs are
+converted into integer values by the function |command_verb|. That
+function is now coded inefficiently; it should eventually be implemented
+using a hash table.
+
+@d nonesuch = 0 { there is no such command }
+@d rln03 = 1 { read LN03 format file }
+@d tln03 = 2 { type LN03 character rasters }
+@d tln03long = 3 { type a longword from the LN03 file }
+@d tln03word = 4 { type a word from the LN03 file }
+@d rpxl = 5 { read \.{PXL} format file }
+@d tpxl = 6 { type \.{PXL} character rasters }
+@d tpxllong = 7 { type a longword from the \.{PXL} file }
+@d tpxlword = 8 { type a word from the \.{PXL} file }
+@d toln03 = 9 { convert the \.{PXL} file to an LN03 file }
+@d wln03 = 10 { write the LN03 buffer out onto a file }
+@d toln03x = 11 { convert the \.{PXL} file to an ``extended'' LN03 file }
+@d exit = 99 { leave FFC }
+
+@<Major procedures@>=
+function command_verb :integer;
+ var i :integer;
+ begin
+
+ @<Pick the verb out in |inline| and lowercase it@>@/
+ if index(the_substr,'rln03') = 1 then command_verb := rln03
+ else if index(the_substr,'tln03long') = 1 then command_verb := tln03long
+ else if index(the_substr,'tln03word') = 1 then command_verb := tln03word
+ else if index(the_substr,'tln03') = 1 then command_verb := tln03
+ else if index(the_substr,'rpxl') = 1 then command_verb := rpxl
+ else if index(the_substr,'tpxllong') = 1 then command_verb := tpxllong
+ else if index(the_substr,'tpxlword') = 1 then command_verb := tpxlword
+ else if index(the_substr,'tpxl') = 1 then command_verb := tpxl
+ else if index(the_substr,'toln03x') = 1 then command_verb := toln03x
+ else if index(the_substr,'toln03') = 1 then command_verb := toln03
+ else if index(the_substr,'wln03') = 1 then command_verb := wln03
+ else if index(the_substr,'exit') = 1 then command_verb := exit
+ else command_verb := nonesuch
+ end;
+
+@ To pick out the command verb, we frequently use two parsing macros,
+|skipb| and |skipnb|, which skip over blanks and non-blanks respectively in
+the |inline| string, advancing the index variable |ilp| as they go.
+
+@d skipb == while (ilp <= illen) and (the_char = ' ') do ilp := ilp+1
+@d skipnb == while (ilp <= illen) and (the_char <> ' ') do ilp := ilp+1
+
+@<Pick the verb...@>=
+skipb; istart := ilp; skipnb;@/
+for i := istart to ilp-1 do begin
+ if (inline[i] >= 'A') and (inline[i] <= 'Z') then
+ inline[i] := chr(ord(inline[i])+ord('a')-ord('A'))
+end;
+
+@ The main loop of the program is a loop of reading and executing commands:
+
+@d cmd_error(#) == begin writeln(#); goto cmd_prompt end
+
+@<The main program@>=
+cmd_prompt:
+ writeln;
+ write('FFC>');
+ readln(inline);@/
+ if length(inline) = 513 then cmd_error('Command line too long');@/
+ inline := inline+' ';
+ ilp := 1;
+ illen := length(inline);@/
+ verb := command_verb;@/
+ if verb = nonesuch then cmd_error('No such command')@/
+ else @<Execute the command@>;@/
+ goto cmd_prompt;
+final_end:
+
+@* Reading LN03 format files. We read the entire LN03 file into a single
+large buffer:
+
+@d ln03_blocks = 512
+@d ln03_buf_size == ((ln03_blocks*512)-1)
+@d ln03_buf == l_buf.c
+
+@<Globals...@>=
+@!ln03_file: byte_file;
+@!ln03_count,ln03_len,i: integer;
+@!l_buf: packed record
+ case boolean of
+ false: (c: packed array[0..ln03_buf_size] of char);
+ true: (b: packed array [0..ln03_blocks-1] of byte_block);
+ end;
+
+@ The file name is obtained from |inline|:
+
+@<Execute...@>=
+if verb = rln03 then begin
+ skipb;
+ istart := ilp;
+ skipnb;@/
+ open(ln03_file,the_substr,@=readonly@>,@=error:=continue@>);
+ if status(ln03_file) > 0 then cmd_error('Couldn''t open file');
+ reset(ln03_file);
+ @<Read the LN03 file@>
+end
+
+@ The LN03 file is read into |inline|, line by line, and copied into the
+|ln03_buf|:
+
+@<Read the LN03 file@>=
+ln03_count:=0;
+while (not eof(ln03_file)) and (ln03_count < ln03_blocks) do
+ begin
+ read(ln03_file,l_buf.b[ln03_count]);
+ ln03_count:=ln03_count+1;
+ end;
+ln03_len:=ln03_count*512;
+close(ln03_file);
+@<Check LN03 file@>
+
+@ It would be desirable to check@^checking@> the LN03 file just read in, to
+warn the user about anomalies. For the moment, though, we just look at the
+opening two longwords. There are lots of other things we would like to do,
+for example: check that all character locators and string descriptors point
+into the right ranges, check that character definitions and strings in the
+pool are in order, check that every character definition has the ``flag
+flag'' set, check that character definitions are aligned and do not
+overlap.
+
+@d ln03_long(#) == (ord(ln03_buf[#]) + (256*(ord(ln03_buf[(#)+1])
+ + 256*(ord(ln03_buf[(#)+2])
+ + 256*ord(ln03_buf[(#)+3])))))
+
+@d ln03_word(#) == (ord(ln03_buf[#]) + 256*ord(ln03_buf[(#)+1]))
+
+@<Check LN03 file@>=
+if ln03_len < 16 then writeln('LN03 file too short');
+if (ln03_buf[4] <> 'F') or (ln03_buf[5] <> 'O') or (ln03_buf[6] <> 'N') or
+ (ln03_buf[7] <> 'T') then writeln('Second longword not FONT')
+
+@* Reading PXL format files. The \.{PXL} format is simple. Again we read
+the entire file into a single large buffer:
+
+@d pxl_blocks = 512
+@d pxl_buf_size == ((pxl_blocks*512)-1)
+@d pxl_buf == p_buf.c
+
+@<Globals...@>=
+@!pxl_file: byte_file;
+@!pxl_count,pxl_len: integer;
+@!p_buf: packed record
+ case boolean of
+ false: (c: packed array[0..pxl_buf_size] of char);
+ true: (b: packed array [0..pxl_blocks-1] of byte_block);
+ end;
+
+@ The file name is obtained from |inline|:
+
+@<Execute...@>=
+else if verb = rpxl then begin
+ skipb;
+ istart := ilp;
+ skipnb;@/
+ open(pxl_file,the_substr,@=readonly@>,@=error:=continue@>);
+ if status(pxl_file) > 0 then cmd_error('Couldn''t open file');
+ reset(pxl_file);
+ @<Read the \.{PXL} file@>
+end
+
+@ The \.{PXL} file is read into |inline|, line by line, and copied into the
+|pxl_buf|:
+
+@<Read the \.{PXL} file@>=
+pxl_count:=0;
+while (not eof(pxl_file)) and (pxl_count < pxl_blocks) do
+ begin
+ read(pxl_file,p_buf.b[pxl_count]);
+ pxl_count:=pxl_count+1;
+ end;
+pxl_len:=pxl_count*512;
+close(pxl_file);
+@<Check \.{PXL} file@>
+
+@ As was the case for the LN03 format file, we would also like to
+check@^checks@> the \.{PXL} file just read in, to warn the user about
+anomalies.
+
+On VMS systems, \.{PXL} files are usually padded with extra longwords at
+the end so their length is a multiple of 512. Thus, we search backwards
+from the end of the file, looking for the ID longword (value 1001), which
+marks the real end of the file. We only search through the last 512 bytes,
+however.
+
+The definitions of |pxl_long| and |pxl_word| take into account the fact
+that the bytes in \.{PXL} file longwords are reversed with respect to the
+normal VAX ordering.
+
+@d pxl_long(#) == (ord(pxl_buf[(#)+3]) + (256*(ord(pxl_buf[(#)+2])
+ + 256*(ord(pxl_buf[(#)+1])
+ + 256*ord(pxl_buf[#])))))
+
+@d pxl_word(#) == (ord(pxl_buf[(#)+1]) + 256*ord(pxl_buf[#]))
+
+@<Check \.{PXL} file@>=
+if not (pxl_len mod 4 = 0) then writeln('PXL file length not multiple of 4');
+if pxl_long(0) <> 1001 then writeln('Initial PXL format id wrong');
+i := pxl_len-4;
+while (i >= pxl_len-512) do begin
+ if pxl_long(i) = 1001 then begin
+ pxl_len := i+4;
+ i := -1
+ end else i := i-4
+end;
+if pxl_len < 16 then writeln('PXL file too short');
+if pxl_long(pxl_len-4) <> 1001 then writeln('Final PXL format id wrong');
+
+@* Typing the character rasters. When FFC gets a raster typing command, it
+may read a character number from |inline| using the function |getfixnum|.
+
+@<Major...@>=
+ function getfixnum :integer;
+ label 1;
+ var x,x1 :integer;
+ negative :boolean;
+
+ begin
+ x1 := 0;
+ negative := false;
+ skipb;
+ if ilp > illen then goto 1;
+ if the_char = '-' then begin
+ negative := true;
+ ilp := ilp+1
+ end;
+
+ if (ilp > illen) or (the_char < '0') or (the_char > '9')
+ then goto 1;
+
+ x1 := 0;
+ while (the_char >= '0') and (the_char <= '9') do begin
+ x1 := 10*x1 + ord(the_char) - ord('0');
+ ilp := ilp+1;
+ if ilp > illen then goto 1;
+ end;
+
+ 1:
+ x := x1;
+ if negative then x := -x;
+ getfixnum := x
+
+ end;
+
+@ The rasters corresponding to a character are stored in the area of the
+LN03 file called the character definitions area. This area is pointed to by
+entries in the character directory. The location of the character directory
+is specified in the font file header. We define symbolic names for all the
+offsets in the font file header:
+
+@d nob_offset = 0
+@d font_offset = nob_offset+4
+@d version_offset = font_offset+4
+@d id_offset = version_offset+4
+@d id_string_offset = id_offset + 8
+@d revision_offset = id_string_offset + 64
+@d date_offset = revision_offset + 4
+@d attributes_offset = date_offset + 12
+@d parameters_offset = attributes_offset + 8
+@d chardir_offset = parameters_offset + 8
+@d segment_list_offset = chardir_offset + 8
+@d future_info_offset = segment_list_offset + 8
+@d string_pool_offset = future_info_offset + 8
+@d kern_info_offset = string_pool_offset + 8
+@d chardefs_offset = kern_info_offset + 8
+@d charcount_offset = chardefs_offset + 8
+@d organization_flags_offset = charcount_offset + 32
+@d size_of_char_params_offset = organization_flags_offset + 4
+@d raster_expansion_info_offset = size_of_char_params_offset + 4
+
+@ With these definitions, we figure out which character the user wants us
+to display, determine where its locator is in the character directory, and
+display the rasters.
+
+@<Execute...@>=
+else if verb = tln03 then begin
+ skipb;
+ if ilp > illen then cmd_error('You have to say what character to type');
+ if (the_char = '''') and (ilp < illen) then i := ord(inline[ilp+1])
+ else i := getfixnum;
+ if (i < ln03_long(charcount_offset)) or
+ (i > ln03_long(charcount_offset+4))
+ then cmd_error('No such character');
+ i := 4*(i-ln03_long(charcount_offset))+ln03_long(chardir_offset+4);
+ @<Find start of rasters for character@>;
+ @<Actually type the rasters@>
+end
+
+@ The variable |def_start| is the location in the file where the character
+definition begins. The rasters themselves begin a few bytes down from
+there, at |ras_start|. The array |visible_byte| is used to hold the visible
+form of a byte from the raster.
+
+@<Global...@>=
+@!def_start, ras_start: integer;
+@!j,k,l,m,n: integer; { scratch variables }
+@!visible_byte: packed array[1..8] of char;
+
+@ The character definition we are seeking is pointed to by a character
+locator@^character locators@> in the character directory portion of the
+LN03 file.
+
+In this version of FFC, we only support locators that point directly into
+the file. The LN03 format also allows ``indirect'' locators that point to
+other files, or to another locator. These are distinguished by a high bit 1
+in the locator longword, whence the test |ln03_buf[i+3] >= chr(@"80)|
+below.
+
+@<Find start...@>=
+ if ln03_buf[i+3] >= chr(@"80)
+ then cmd_error('Indirect locators not supported');
+ def_start := ord(ln03_buf[i])+256*ln03_word(i+1);
+ if def_start = 0 then cmd_error('Locator is zero');
+ ras_start := def_start+ln03_long(size_of_char_params_offset);
+ writeln(' ma = ',ras_start:1);
+ if ras_start > ln03_buf_size
+ then cmd_error('Character outside piece of file read')
+
+@ The LN03 format allows the raster to be stored in a number of ways. The
+rasters may be placed in one of eight different ``orientations,''
+corresponding to repeated $90$ degree orthogonal rotation and mirroring.
+When we type out the rasters, we just show things the way they are in the
+file. All we need to know is if the orientation is landscape or portrait,
+which can be tested by determining of the orientation value is odd or even.
+
+In addition to the different orientations, the format supports run length
+encoding@^run length encoding@>. The current version of FFC does not,
+however. We therefore require that the ``Type1'' field in the rasters equal
+|@"81|, which indicates no run length encoding:
+
+@<Actually type...@>=
+ writeln('width ',((ln03_long(def_start+4))/24.0):5:1,' pixels');
+ if ord(ln03_buf[ras_start+1]) <> @"81
+ then cmd_error('Run length encoding not supported');
+ if odd(ord(ln03_buf[ras_start])) then begin { landscape }
+ writeln('landscape');
+ i := ln03_word(ras_start+4);
+ j := ln03_word(ras_start+6)
+ end else begin { portrait }
+ writeln('portrait');
+ i := ln03_word(ras_start+6);
+ j := ln03_word(ras_start+4)
+ end;
+ writeln(i:1,' rows ',j:1,' columns');
+
+ k := i div 8;
+ if i <> 8*k then k := k+1;
+ for l := 0 to j-1 do begin
+ for m := 0 to k-1 do begin
+ binrep(ord(ln03_buf[ras_start+8+k*l+m]));
+ write(visible_byte)
+ end;
+ writeln
+ end
+
+@ The |binrep| procedure used in the preceding section writes the visible
+representation of a bitmap byte into the array |visible_byte|. The visible
+representation is binary, padded out to a width of 8, using a `\.{B}' to
+represent 1 and a period `\.{.}' to represent 0.
+
+@<Major...@>=
+procedure binrep (v: integer);
+ var cnt, rem, quo: integer;
+begin
+ visible_byte := '........';
+ quo := v;
+ for cnt := 1 to 8 do begin
+ rem := quo mod 2;
+ quo := quo div 2;
+ if rem <> 0 then visible_byte[cnt] := 'B'
+ end;
+end;
+
+@ The code up to now has been concerned with typing rasters from LN03
+files. \.{PXL} files are similar but simpler. The orientation is always
+portrait. The directory information is always located at a fixed position
+with respect to the end of the file.
+
+@<Execute...@>=
+else if verb = tpxl then begin
+ skipb;
+ if ilp > illen then cmd_error('You have to say what character to type');
+ if (the_char = '''') and (ilp < illen) then i := ord(inline[ilp+1])
+ else i := getfixnum;
+ if (i < 0) or (i > 127) then cmd_error('No such character');
+ def_start := pxl_len - 4*517 + 16*i;
+ ras_start := 4*pxl_long(def_start+8);
+ writeln(' rasters at ',ras_start:1);
+ if ras_start > pxl_len then cmd_error('Rasters outside file');
+ i := pxl_word(def_start);
+ j := pxl_word(def_start+2);
+ writeln(i:1,' columns ',j:1,' rows');
+ k := i div 32;
+ if i <> 32*k then k := k+1;
+ for l := 0 to j-1 do begin
+ for m := 0 to k-1 do begin
+ rev_binrep(ord(pxl_buf[ras_start+4*k*l+4*m]));
+ write(visible_byte);
+ rev_binrep(ord(pxl_buf[ras_start+4*k*l+4*m+1]));
+ write(visible_byte);
+ rev_binrep(ord(pxl_buf[ras_start+4*k*l+4*m+2]));
+ write(visible_byte);
+ rev_binrep(ord(pxl_buf[ras_start+4*k*l+4*m+3]));
+ write(visible_byte)
+ end;
+ writeln
+ end
+end
+
+@ A different |binrep| procedure is wanted here, one that puts the
+bits into |visible_byte| in opposite order:
+
+@<Major...@>=
+procedure rev_binrep (v: integer);
+ var cnt, rem, quo: integer;
+begin
+ visible_byte := '........';
+ quo := v;
+ for cnt := 1 to 8 do begin
+ rem := quo mod 2;
+ quo := quo div 2;
+ if rem <> 0 then visible_byte[9-cnt] := 'B'
+ end;
+end;
+
+
+@* Typing raw data from the file. For preliminary debugging purposes, it is
+useful to have the ability to type raw data from a font file. This is the
+purpose of the following simple code:
+
+@<Execute...@>=
+else if verb = tln03long then begin
+ i := getfixnum;
+ if (i >= 0) and (i < ln03_len) then writeln(ln03_long(i):1)
+ else writeln('Location not in file')
+end
+else if verb = tln03word then begin
+ i := getfixnum;
+ if (i >= 0) and (i < ln03_len) then writeln(ln03_word(i))
+ else writeln('Location not in file')
+end
+else if verb = tpxllong then begin
+ i := getfixnum;
+ if (i >= 0) and (i < pxl_len) then writeln(pxl_long(i):1)
+ else writeln('Location not in file')
+end
+else if verb = tpxlword then begin
+ i := getfixnum;
+ if (i >= 0) and (i < pxl_len) then writeln(pxl_word(i))
+ else writeln('Location not in file')
+end
+
+@* Conversion to LN03 form. When converting a font from \.{PXL} to LN03
+form, in addition to converting the character rasters, we have to fill
+in a number of font parameter slots. These slots are located in the
+first three areas of the font file. We already defined offset constants
+into the first area, the font file header. Here are some offsets into the
+other two areas, the ``font attributes'' and the ``font parameters.''
+
+@d flags_offset = raster_expansion_info_offset + 48
+@d strings_offset = flags_offset + 4
+@d type_size_offset = strings_offset + 48
+@d foundry_offset = type_size_offset + 36
+@d param_flags_offset = foundry_offset + 16
+@d lining_offset = param_flags_offset + 4
+@d subsup_offset = lining_offset + 32
+@d hspace_offset = subsup_offset + 16
+@d vspace_offset = hspace_offset + 32
+@d charxdir_offset = vspace_offset + 40
+
+@ When we are asked to convert to LN03 form, we first clear the |ln03_buf|,
+then we convert the rasters, and finally we fill the parameters. For the
+moment, to comply with the LN03 format's restriction to 94 characters, we
+only convert characters 33 through 126 of the \.{PXL} file. The rest are
+discarded. If the command is \.{toln03x}, however, the entire 128
+characters are copied, violating the LN03 format standard.
+
+@<Execute...@>=
+else if (verb = toln03) or (verb = toln03x) then begin
+ for i := 0 to ln03_buf_size do ln03_buf[i] := chr(0);
+ if verb = toln03 then begin first_char := 33; last_char := 126 end
+ else begin first_char := 0; last_char := 127 end;
+ num_chars := last_char-first_char+1;
+ @<Convert \.{PXL} rasters to LN03 form@>;
+ @<Fill LN03 parameters@>
+end
+
+@ We have to know where to put the rasters as we convert them, which means
+we have to know where the ``character definitions'' area begins in the LN03
+file. That area comes after the character directory (4 bytes per
+character), the table of font segments (4 bytes, to indicate there are
+no font segments), the future info section (empty), the string pool,
+and the kerning info (empty).
+
+The string pool has to contain the character set designator (7 bytes), type
+family ID (7 bytes), type family name, Font ID (16 bytes), type category,
+foundry, font designer, and segment names. For the moment, converted files
+have a 16-byte type family name and empty type category, foundry, font
+designer and segment names. The total length is 46 bytes, rounded up to 48
+bytes.
+
+To fill some slots in the LN03 format, we have to add up the sizes of the
+rasters in three different ways---portrait, landscape and
+``mixed''---whence the variables |psize|, |lsize| and |msize|.
+
+@<Globals...@>=
+@!charxdef_offset,string_pool_size,stringx_pool_offset: integer;
+@!psize,lsize,msize: integer;
+@!first_char,last_char,num_chars: integer;
+@!all_blank: boolean;
+@!zchar,tfm_width,x_offset,y_offset: integer;
+@!dsize,mag: real;
+
+@ The |charxdef_offset| can be computed from the information above. (We
+could have defined this offset as a constant, but want to leave ourselves a
+little more flexibility.)
+
+@<Convert \.{PXL} rasters...@>=
+ string_pool_size := 48;
+ stringx_pool_offset := charxdir_offset+4*num_chars+4;
+ charxdef_offset := stringx_pool_offset+string_pool_size;
+ ln03_len := charxdef_offset;
+ psize := 0; lsize := 0; msize := 0;
+ for zchar := first_char to last_char do
+ begin @<Convert \.{PXL} for |zchar|@> end;
+
+@ When converting for a single character, we first fill in the ``flag
+flag'' of the character, which the format requires us to set. Then we copy
+the nominal width, left bearing and raster baseline.
+
+The LN03 format demands that these dimensions be converted to
+``centipoints,'' where a ``centipoint@^centipoint (unit of measure)@>'' is
+defined to be one 7200$^{\rm th}$ of an inch. The nominal width is stored
+in the \.{PXL} file as a fraction of the magnification times design size.
+The magnification, multiplied by five times the dots per inch (in our case
+1500), is stored in the fourth-from-last longword of the \.{PXL} file. The
+design size is stored in the third-from-last longword in units of $2^{-20}$
+``points,'' a ``point'' being defined as one 72.27$^{\rm th}$ of an inch.
+
+The left bearing and raster baseline are stored in pixel units in the
+\.{PXL} file and in centipoints in the LN03 file, so the conversion
+factor is $7200/300 = 24$.
+
+@d two_to_the_20th == (@"100000)
+
+@<Convert \.{PXL} for ...@>=
+ ln03_buf[ln03_len+3] := chr(@"80);
+ def_start := pxl_len - 4*517 + 16*zchar;
+ ras_start := 4*pxl_long(def_start+8);
+ if ras_start > pxl_len then cmd_error('Rasters outside file for ',
+ zchar:1);
+ tfm_width := pxl_long(def_start+12);
+ dsize := pxl_long(pxl_len-12);
+ mag := pxl_long(pxl_len-16);
+ dsize := (dsize/two_to_the_20th)*(mag/1500.0);
+ x_offset := signed_pxl_word(def_start+4);
+ y_offset := signed_pxl_word(def_start+6);
+ set_ln03_long(ln03_len+4,round((dsize*7200.0*tfm_width)/
+ (two_to_the_20th*72.27)));
+ set_ln03_long(ln03_len+8,-24*x_offset);
+ set_ln03_long(ln03_len+12,-24*y_offset);
+
+@ Even though we're not trying to make this program portable, we do
+the word and longword computations by hand, instead of using overlays.
+
+@<Major...@>=
+ function signed_pxl_word (index: integer) :integer;
+ begin
+ if ord(pxl_buf[index]) >= @"80 then
+ signed_pxl_word := pxl_word(index) - @"10000
+ else signed_pxl_word := pxl_word(index)
+ end;
+
+ procedure set_ln03_long (index, value: integer);
+ var negative: boolean;
+ a,b,c,d,carry: integer;
+ begin
+ if value < 0 then negative := true else negative := false;
+ if negative then value := -value;
+ a := value mod 256;
+ b := (value div 256) mod 256;
+ c := (value div (256*256)) mod 256;
+ d := value div (256*256*256);
+ if negative then begin
+ carry := 0;
+ a := 256-a;
+ if a = 256 then begin a := 0; carry := 1 end;
+ b := 255+carry-b;
+ if b = 256 then b := 0 else carry := 0;
+ c := 255+carry-c;
+ if c = 256 then c := 0 else carry := 0;
+ d := 255+carry-d;
+ if d = 256 then d := 0
+ end;
+ ln03_buf[index] := chr(a);
+ ln03_buf[index+1] := chr(b);
+ ln03_buf[index+2] := chr(c);
+ ln03_buf[index+3] := chr(d)
+ end;
+
+@ The rasters are always placed in portrait into the LN03 file, with no use
+of run-length encoding, so the ``orient'' field of the raster format is 0,
+and the ``Type1'' field is @"81. We copy the rasters byte by byte from the
+\.{PXL} buffer, reversing each byte. At the end we store the raster address
+in the character directory and increment the |pxl_len| variable, making
+sure that all character definitions fall on even-byte boundaries. We also
+increment the size variables.
+
+If the rasters for a character are completely blank, an ``undocumented
+feature'' of the LN03 seems to cause things to go wrong. Thus, in that
+case, we set height and width to 1, and put in a blank byte.
+
+@<Convert \.{PXL} for...@>=
+ ln03_buf[ln03_len+17] := chr(@"81);
+ i := pxl_word(def_start);
+ j := pxl_word(def_start+2);
+ all_blank := (i = 0) and (j = 0);
+ if all_blank then begin i := 1; j := 1 end;
+ ln03_buf[ln03_len+20] := chr(j mod 256);
+ ln03_buf[ln03_len+21] := chr(j div 256);
+ ln03_buf[ln03_len+22] := chr(i mod 256);
+ ln03_buf[ln03_len+23] := chr(i div 256);
+ k := i div 32;
+ if i <> 32*k then k := k+1;
+ n := i div 8;
+ if i <> 8*n then n := n+1;
+ if not all_blank then
+ for l := 0 to j-1 do
+ for m := 0 to n-1 do
+ ln03_buf[ln03_len+24+n*l+m] :=
+ reverse_byte(pxl_buf[ras_start+4*k*l+m]);
+ set_ln03_long(charxdir_offset+4*(zchar-first_char),ln03_len);
+ ln03_len := ln03_len+24+j*n;
+ if odd(ln03_len) then ln03_len := ln03_len+1;@/
+ { now update the size counts }
+ psize := psize+j*n;
+ k := j div 8;
+ if j <> 8*k then k := k+1;
+ lsize := lsize+i*k;
+ if i*k > j*n then msize := msize+i*k else msize := msize+j*n;
+
+@ It is necessary to use a function |reverse_byte| to reverse the bits in
+going from the \.{PXL} to the LN03 raster format. It would be faster to do
+this with a 256-byte string.
+
+@<Major...@>=
+ function reverse_byte (u: char) : char;
+ var cnt,rv :integer;
+ begin
+ binrep(ord(u));
+ rv := 0;
+ for cnt := 1 to 8 do
+ if visible_byte[cnt] = 'B' then rv := 1 + 2*rv
+ else rv := 2*rv;
+ reverse_byte := chr(rv)
+ end;
+
+@* Filling in the LN03 parameters. Once the rasters are transferred from
+\.{PXL} to LN03 format, we have to fill in the endless LN03 parameters,
+using the offsets defined above. We will only fill parameters that are not
+left at zero, since we already initialized the |ln03_buf| to zero above. We
+start off with the header and trailer, placing |'FONT'| and the |ln03_len|
+in the opening and closing longwords.
+
+@<Fill LN03 parameters@>=
+ if ln03_len mod 4 <> 0 then ln03_len := ln03_len + (4 - (ln03_len mod 4));
+ ln03_len := ln03_len+8; { for trailer }
+ set_ln03_long(0,ln03_len);
+ set_ln03_long(ln03_len-8,ln03_len);
+ ln03_buf[4] := 'F';
+ ln03_buf[ln03_len-4] := 'F';
+ ln03_buf[5] := 'O';
+ ln03_buf[ln03_len-3] := 'O';
+ ln03_buf[6] := 'N';
+ ln03_buf[ln03_len-2] := 'N';
+ ln03_buf[7] := 'T';
+ ln03_buf[ln03_len-1] := 'T';
+
+@ There follow the version number of the format (always 1), the font file
+ID (always the value indicated below), the revision number of the font
+(always 0), and the date and time record (always 2:00 {\eightrm P.M.},
+September 11, 1973). Eventually the user will be able to set the font file
+ID with a command, and the time will come from the timestamp on the \.{PXL}
+file.
+
+@<Fill LN03 parameters@>=
+ ln03_buf[version_offset] := chr(1);
+ ln03_buf[id_offset] := chr(31);
+ ln03_buf[id_offset+4] := chr(id_offset+8);
+ font_id_string := 'U000000002SK00GG0001UZZZZ02F000';
+ for i := 1 to 31 do ln03_buf[id_string_offset+i-1] :=
+ font_id_string[i];
+ ln03_buf[date_offset] := chr(1973 mod 256);
+ ln03_buf[date_offset+1] := chr(1973 div 256);
+ ln03_buf[date_offset+2] := chr(9);
+ ln03_buf[date_offset+4] := chr(11);
+ ln03_buf[date_offset+6] := chr(14);
+
+@ Oops, we forgot to declare the |font_id_string|:
+
+@<Globals...@>=
+@!font_id_string: packed array [1..31] of char;
+
+@ After the date and time come the various pointers into the file.
+
+@<Fill LN03 parameters@>=
+ set_ln03_long(attributes_offset,param_flags_offset-flags_offset);
+ set_ln03_long(attributes_offset+4,flags_offset);
+ set_ln03_long(parameters_offset,charxdir_offset-param_flags_offset);
+ set_ln03_long(parameters_offset+4,param_flags_offset);
+ set_ln03_long(chardir_offset,4*num_chars);
+ set_ln03_long(chardir_offset+4,charxdir_offset);
+ set_ln03_long(segment_list_offset,4);
+ set_ln03_long(segment_list_offset+4,charxdir_offset+4*num_chars);
+ set_ln03_long(future_info_offset+4,charxdir_offset+4*num_chars+4);
+ set_ln03_long(string_pool_offset,string_pool_size);
+ set_ln03_long(string_pool_offset+4,charxdir_offset+4*num_chars+4);
+ set_ln03_long(kern_info_offset+4,charxdir_offset+4*num_chars+4+
+ string_pool_size);
+ set_ln03_long(chardefs_offset,ln03_len-8-charxdef_offset);
+ set_ln03_long(chardefs_offset+4,charxdef_offset);
+
+@ Next come the eight longwords of character count information, the
+``organization'' flags, the size of character parameters, and the raster
+expansion information.
+
+@<Fill LN03 parameters@>=
+ set_ln03_long(charcount_offset,first_char); { first character }
+ set_ln03_long(charcount_offset+4,last_char); { last character }
+ set_ln03_long(charcount_offset+28,32); { space encoding }
+ set_ln03_long(charcount_offset+32,@"A8); { ``organization'' flags }
+ set_ln03_long(charcount_offset+36,16); { size of character params. }
+ set_ln03_long(raster_expansion_info_offset,num_chars); { in file locator count }
+ set_ln03_long(raster_expansion_info_offset+8,num_chars); { number of character defs. }
+ set_ln03_long(raster_expansion_info_offset+16,num_chars); { number of rasters }
+ set_ln03_long(raster_expansion_info_offset+24,psize);
+ set_ln03_long(raster_expansion_info_offset+28,lsize);
+ set_ln03_long(raster_expansion_info_offset+32,msize);
+
+@ Next come the font attributes. These include a number of strings that
+have to be placed in the string pool. Of the flags, we set the Roman one
+only. The character set designator is always set to |'0B'|, tab, |'ZZZZ'|.
+Again, eventually the user will have commands to set these.
+
+@<Fill LN03 parameters@>=
+ set_ln03_long(flags_offset,2); { flags }
+ set_ln03_long(flags_offset+4,7); { length of character set designator }
+ set_ln03_long(flags_offset+8,stringx_pool_offset);
+ ln03_buf[stringx_pool_offset] := '0';
+ ln03_buf[stringx_pool_offset+1] := 'B';
+ ln03_buf[stringx_pool_offset+2] := chr(9);
+ ln03_buf[stringx_pool_offset+3] := 'Z';
+ ln03_buf[stringx_pool_offset+4] := 'Z';
+ ln03_buf[stringx_pool_offset+5] := 'Z';
+ ln03_buf[stringx_pool_offset+6] := 'Z';
+ set_ln03_long(flags_offset+12,7); { length of type family ID }
+ set_ln03_long(flags_offset+16,stringx_pool_offset+7);
+ for i := 1 to 7 do
+ ln03_buf[stringx_pool_offset+7+i-1] := font_id_string[i];
+ set_ln03_long(flags_offset+20,16); { length of type family name }
+ set_ln03_long(flags_offset+24,stringx_pool_offset+7+7);
+ for i := 1 to 16 do
+ ln03_buf[stringx_pool_offset+7+7+i-1] := ' ';
+ set_ln03_long(flags_offset+28,16); { length of font id }
+ set_ln03_long(flags_offset+32,stringx_pool_offset+7+7+16);
+ for i := 1 to 16 do
+ ln03_buf[stringx_pool_offset+7+7+16+i-1] := font_id_string[i];
+
+@ The type size is given in the \.{PXL} file in units of $2^{-20}$ points;
+we have to translate it to points. The average character width is
+arbitrarily set to half the point size (which means we multiply it by 50 to
+convert it to centipoints).
+
+@<Fill LN03 parameters@>=
+ i := pxl_long(pxl_len-12);
+ points := i div two_to_the_20th;
+ k := (10000*i mod two_to_the_20th) div two_to_the_20th;
+ ln03_buf[type_size_offset] := chr(points mod 256);
+ ln03_buf[type_size_offset+1] := chr(points div 256);
+ ln03_buf[type_size_offset+2] := chr(k mod 256);
+ ln03_buf[type_size_offset+3] := chr(k div 256);
+ if k > 4999 then points := points+1;
+ set_ln03_long(type_size_offset+4,50*points);
+
+@ We need to declare |points|, by the way, which stores the pointsize of
+the font as an integer.
+
+@<Globals...@>=
+@!points: integer;
+
+@ Next come the resolution, weight, horizontal proportion, horizontal
+proportion fraction, aspect ratio and character up vector.
+
+@<Fill LN03 parameters@>=
+ ln03_buf[type_size_offset+10] := chr(24); { hocus-pocus means 300 dpi }
+ ln03_buf[type_size_offset+12] := chr(16); { weight is ``regular'' }
+ ln03_buf[type_size_offset+16] := chr(16); { horizontal proportion is regular}
+ ln03_buf[type_size_offset+20] := chr(1);
+ ln03_buf[type_size_offset+22] := chr(1);@/
+ ln03_buf[type_size_offset+24] := chr(1); { aspect ratio is 1/1}
+ ln03_buf[type_size_offset+26] := chr(1);
+ ln03_buf[type_size_offset+30] := chr(1); { character up vector }
+
+@ The third and last step of the filling process handles the font
+parameters, which are mostly supposed to be in ``centipoints.'' Eventually
+some of these will be obtained from the \.{TFM} file, but for the moment we
+wing it, guessing them all from the point size |points|.
+
+The flag longword that begins the font parameter section can conveniently
+be left empty. The slant is not guessed, but always set to zero. As you can
+see, the values of the horizontal and vertical spacing parameters are
+fairly random, but should not lead to impossibly poor output.
+
+@<Fill LN03 parameters@>=
+ set_ln03_long(lining_offset,12*points); { underline offset }
+ set_ln03_long(lining_offset+4,8*points); { underline thickness }
+ set_ln03_long(lining_offset+8,-25*points); { strike through offset }
+ set_ln03_long(lining_offset+12,8*points); { strike through thickness }
+ set_ln03_long(lining_offset+16,-60*points); { overline offset }
+ set_ln03_long(lining_offset+20,8*points); { overline thickness }
+ ln03_buf[lining_offset+26] := chr(1); { slant (always 0) }
+ ln03_buf[lining_offset+30] := chr(points*12 mod 256); { shadow vector }
+ ln03_buf[lining_offset+31] := chr(points*12 div 256);
+ set_ln03_long(subsup_offset,-36*points);
+ set_ln03_long(subsup_offset+8,16*points);
+ set_ln03_long(hspace_offset,24*points); { center line }
+ set_ln03_long(hspace_offset+4,20*points); { minimum space width }
+ set_ln03_long(hspace_offset+8,80*points); { maximum space width }
+ set_ln03_long(hspace_offset+12,25*points); { width of space }
+ set_ln03_long(hspace_offset+16,100*points); { width of em }
+ set_ln03_long(hspace_offset+20,50*points); { width of en }
+ set_ln03_long(hspace_offset+24,10*points); { width of thinspace }
+ set_ln03_long(hspace_offset+28,35*points); { width of digit }
+ set_ln03_long(vspace_offset,-64*points); { top line }
+ set_ln03_long(vspace_offset+4,-50*points); { floating accent line }
+ set_ln03_long(vspace_offset+8,-35*points); { half line }
+ set_ln03_long(vspace_offset+12,100*points); { total vertical size }
+ set_ln03_long(vspace_offset+16,-65*points); { above baseline }
+ set_ln03_long(vspace_offset+20,35*points); { below baseline }
+ set_ln03_long(vspace_offset+24,65*points); { cap H height }
+ set_ln03_long(vspace_offset+28,35*points); { small x height }
+ set_ln03_long(vspace_offset+32,10*points); { white space above tallest }
+ set_ln03_long(vspace_offset+36,10*points); { white space below deepest }
+
+@* Writing the LN03 file. It seems to be conventional to store LN03 format
+font files as 512-byte fixed length record VAX files. For this reason, we
+declare |outfile| to be this type of file:
+
+@<Globals...@>=
+@!outfile: byte_file;
+
+@ The writing simply consists of putting chunks of the |ln03_buf| out
+into |outfile|.
+
+@<Execute...@>=
+else if verb = wln03 then begin
+ skipb;
+ if ilp > illen then cmd_error('You must specify a file to write into');
+ istart := ilp; skipnb;
+ open(outfile,the_substr,@=error:=continue@>);
+ if status(outfile) <> 0 then cmd_error('couldn''t open',the_substr);
+ rewrite(outfile);
+ i := ln03_len div 512;
+ if ln03_len <> i*512 then i := i+1;
+ for j := 0 to i-1 do
+ write(outfile,l_buf.b[j]);
+ close(outfile)
+end
+
+@* Leaving the program. If the user types an |exit| command, we leave:
+
+@<Execute...@>=
+else if verb = exit then goto final_end
+
+@* Index. This is the standard Web index to all identifiers.
diff --git a/dviware/ln03/rose/forpxl.fdl b/dviware/ln03/rose/forpxl.fdl
new file mode 100644
index 0000000000..121208c1c5
--- /dev/null
+++ b/dviware/ln03/rose/forpxl.fdl
@@ -0,0 +1,4 @@
+IDENT "14-NOV-1985 11:21:24 VAX-11 FDL Editor"
+
+RECORD
+ CARRIAGE_CONTROL none
diff --git a/dviware/ln03/rose/ln03bis.mem b/dviware/ln03/rose/ln03bis.mem
new file mode 100644
index 0000000000..813bc399e9
--- /dev/null
+++ b/dviware/ln03/rose/ln03bis.mem
@@ -0,0 +1,632 @@
+The purpose of this file is to explain how to print TeX
+files on your LN03 laser printer. To do this, you must have
+at least one optional RAM cartridge in your LN03.
+
+The TeX to LN03 driver has been changed substantially
+compared to the ones in previous distributions. Error
+reporting has been revamped, and the driver now reads PXL
+format font files as well as LN03 format font files.
+
+The TeX-to-LN03 driver described here requires VAX/VMS V4.2.
+(It has also been tested on Ultrix-32 V1.2 and on 4.3bsd
+Unix; see section 12, "Unix", below.)
+
+Most of the things described below probably also work on
+VAX/VMS V4.0 or V4.1, if you do your linking on a system
+which has VAX C installed.
+
+
+0. BILL OF MATERIALS
+
+The present file is:
+
+ ln03.mem
+
+These are the files that comprise the TeX to LN03 driver:
+
+ dvi2ln3.c
+ dvi2ln3nft.c
+ specials.c
+ clink.opt
+
+These other files comprise an auxiliary program that can be
+helpful:
+
+ newffc.c
+
+Depending on circumstances, you may have received .obj files
+corresponding to the different .c's, or you may need to
+regenerate them using a C compiler.
+
+
+
+1. INSTALLATION
+
+The basic steps to install the DVI to LN03 driver are as follows:
+
+ A. Link the driver program.
+
+ B. Set up your font raster directories.
+
+ C. Have users set up certain definitions in their login.com, or put
+ them in the system login file.
+
+No VMS privileges are needed to install the driver.
+
+
+STEP A, LINKING DVI2LN3. Type:
+
+ $ link dvi2ln3,dvi2ln3nft,specials,clink/opt
+
+
+STEP B, PXL FILES. If you don't already have a collection of font
+files in PXL format, you need to assemble one. The usual way to
+get PXL files is either off a TeX tape, or by running the GFtoPXL
+program which is supplied with Metafont.
+
+Your PXL fonts must have extensions .yyyyPXL, where yyyy is the
+magnification times 1500. For example, the fonts that are at 100%
+magnification have extension .1500PXL, and those that are at 120%
+magnification have extension .1800PXL. Be sure, of course, that
+the fonts are suitable for 300 dot per inch printers, because
+that's what the LN03 is.
+
+VMS allows two files to have the same data bytes, which are
+interpreted in different ways depending on the "record format",
+"record attributes", and "record size". There are many
+possibilities for these parameters. Dvi2ln3 has been tested with
+PXL files of the following two types:
+
+ record format: stream_LF
+
+or
+
+ record format: fixed, record size: 4
+
+You determine these characteristics of a given file by doing a
+VMS DIRECTORY /FULL on the file.
+
+I believe the files come off the TeX tape in the second format.
+If for some reason, your PXL files are not in one of these
+formats, you should test dvi2ln3 to see if it works. If it
+doesn't, you will have to convert your files somehow. Please
+consult section 9 below for some hints on how to do this.
+
+
+STEP C, DEFINITIONS. Everyone who uses the DVI to LN03 converter
+must have the following definitions:
+
+ $ dvi2ln3 == "$tex$:dvi2ln3"
+ $ define tex$pxldir <wherever your PXL files are>
+
+The logical names tex$ and tex$fonts are also needed; these
+should be defined as for running TeX itself.
+
+
+
+2. USING THE DVI TO LN03 DRIVER
+
+To convert foo.dvi to an LN03 format file (extension .ln3), type
+
+ $ dvi2ln3 foo
+
+Print the file foo.ln3 on your LN03 by saying
+
+ $ print/nofeed/queue=<queuename> foo.ln3
+
+This assumes your system manager has set up the LN03 as a spooled
+device with queuename <queuename>. The /nofeed is very important,
+for if you omit it, you may get unwanted page breaks in your
+output file.
+
+
+
+3. COMMAND LINE OPTIONS FOR DVI2LN3
+
+The DVI2LN3 program has some options. These are typed on the
+command line, after the file name FOO, and separated from it by
+at least one space.
+
+ /S=page number - the output file will only contain the pages in
+ the DVI file that start at the given page number. The
+ page number is the value of counter 0.
+
+ /N=number of pages - the output file will only contain that many
+ pages from the DVI file.
+
+For example, to cause only pages 20 through 22 of FOO to be
+converted to LN03 form, say
+
+ $ DVI2LN3 FOO /S=20 /N=3
+
+ /H=<number> - print the file at a horizontal offset of <number>
+ pixels, i.e., moved right that many pixels. (There are 300 pixels to
+ the inch.)
+
+ /V=<number> - print the file at a vertical offset of <number>
+ pixels, i.e., moved down that many pixels.
+
+The values given with /V and /H are ADDED to the TeX parameters
+\hoffset and \voffset; they do not replace these parameters.
+
+ /L - print the entire file in landscape mode (with text running
+ along the long direction of the paper). The size of the text
+ is not changed. Thus you have only about 8 inches from top to
+ bottom - make sure your .tex file does not produce pages which
+ are longer than that.
+
+
+
+4. WARNINGS ABOUT THE LN03 DRIVER
+
+Now, some caveats and warnings about the LN03 driver for TeX.
+
+The driver is copyrighted data of Digital Equipment Corporation.
+Because Digital makes this software available for free, it does
+not promise that the software works or does anything.
+
+A different, supported TeX to LN03 driver may be available from
+Kellerman and Smith in Portland, Oregon.
+
+The most common problem people encounter when first using the
+LN03 support is forgetting to print the .ln3 files with the
+/nofeed qualifier. This can cause a page of output to be split
+into two or more pages.
+
+There are some limitations to the LN03 support:
+
+ - Your LN03 should have at least one RAM cartridge plugged in.
+ Otherwise, it is unlikely that any but the smallest documents
+ will print successfully.
+
+ - The landscape support is very recent.
+
+ - The code that reads PXL files is relatively recent. (Formerly,
+ the driver read LN03 format files only.)
+
+ - One cannot access the preloaded or ROM fonts in an LN03.
+
+ - DVI2LN3 has limitations on the size of a font load, which
+ may be exceeded by TeX files. This rarely occurs.
+
+ - The LN03 also has limitations on the complexity of a page, which
+ may be exceeded by TeX files. This is very rare.
+
+ - Very large fonts such as AMINCH cannot be handled. (The LN03 can't
+ handle them anyway.)
+
+ - For TeX's \magstephalf, the PXL files should have
+ extension .1642PXL. I believe this to be the
+ correct extension, but some distributions give
+ the \magstephalf fonts extension .1643PXL.
+
+ - The VMS spooler for the LN03 has to be set up properly with
+ $ SET TERMINAL and $ SET QUEUE commands. Here are some settings
+ that have been found to work:
+
+Terminal queue LN03$B12, on BOSTON$TTE6:
+ /BASE_PRIORITY=4 /DEFAULT=(FEED) /NOENABLE_GENERIC
+ /FORM=DEFAULT Lowercase /OWNER=[SYSTEM] /PROTECTION=(S:E,O:D,G:R,W:RW)
+ /SCHEDULE=(NOSIZE) /SEPARATE=(FLAG,RESET=(PORTRAIT))
+
+Terminal: _TTE6: Device_Type: Unknown Owner: SYMBIONT_0001
+ Username: SYSTEM
+Terminal Characteristics:
+ Interactive Echo Type_ahead No Escape
+ No Hostsync TTsync Lowercase No Tab
+ No Wrap Scope No Remote Eightbit
+ No Broadcast No Readsync Form Fulldup
+ No Modem No Local_echo No Autobaud No Hangup
+ No Brdcstmbx No DMA No Altypeahd Set_speed
+ No Line Editing Overstrike editing No Fallback No Dialup
+ No Secure server Disconnect No Pasthru No Syspassword
+ No SIXEL Graphics No Soft Characters No Printer Port Numeric Keypad
+ No ANSI_CRT No Regis No Block_mode No Advanced_video
+ No Edit_mode No DEC_CRT No DEC_CRT2
+Device spooled to _CECIL$DUA7:
+
+
+6. MORE ABOUT FONTS
+
+If you should acquire LN03-format fonts by some means other than
+PXL file manipulation, you can use them with dvi2ln3. Just put
+them in a directory and define a logical tex$ln03dir to point to
+them.
+
+You will need to get TFM files for those fonts by some means. The
+NEWFFC program can help you (a little). If you have a font
+foo.ln03, and you want to know what the character with code xx
+looks like in that font, you can type:
+
+ $ run newffc
+
+ FFC>rnft foo.ln03
+ FFC>tnftr xx
+ [Types rasters and tells you the width of the character]
+ FFC>exit
+ $
+
+Using the raster width information, you can then compose a PL
+format file, which can then be converted to TFM format by running
+the PLtoTF program on it. PLtoTF is distributed with TeX.
+
+If you have a VT125, VT240 or VT241 terminal, you can type pnftr
+instead of tnftr; that plots the rasters on the screen using the
+terminal's graphics capabilities.
+
+To link newffc, type
+
+ $ link newffc,clink/opt
+
+or else
+
+ $ link newffc,sys$library:vaxcrtl/lib
+
+
+7. \special COMMANDS
+
+DVI2LN3 interprets certain \special commands.
+
+Right now, the following \special's are implemented:
+
+ ln03:defpoint integer ( [dimension] , [dimension] )
+ ln03:connect integer [/ integer] integer [/ integer] [dimension]
+ ln03:plotfile filename
+ ln03:resetpoints integer integer
+
+A dimension is a number with optional decimal point, followed by
+one of pt, in, pc, cm, mm, bp, dd, cc, and mi.
+
+The peculiar syntax of these commands is due to a request to
+emulate certain commands invented by Textset, Inc. (Ann Arbor,
+Michigan).
+
+Ln03:plotfile includes the named file literally in the .LN3
+output file. No parsing whatsoever of the file is done, nor are
+any defaults applied to the filename.
+
+The remaining specials work with an array of 255 points, numbered
+1 to 255. Each point is a location on the paper. Initially, the
+array is uninitialized; and any contiguous segment of the array
+may be returned to the uninitialized state using ln03:resetpoints
+a b, which uninitializes points a through b inclusive.
+
+A point value may be assigned to a particular position in the
+array using ln03:defpoint. In using ln03:defpoint, one may
+specify the coordinates of the point, or one may omit either the
+x- or the y-coordinate or both, in which case they are taken to
+be the current values at the time the \special call occurs.
+
+The ln03:connect special is used to draw a rule connecting two
+points. The two points must have either the same x-coordinate or
+the same y- coordinate. The optional dimension is the thickness
+of the rule; if none is specified, the rule is 2 pixels thick.
+
+
+8. DVI2LN3 ERROR MESSAGES
+
+Dvi2ln3 internal error, bad txfno or lnfno
+
+ Indicates a bug in dvi2ln3.
+
+Couldn't find good font file: using blanks
+
+ There was neither an LN03 format font file nor a PXL format font
+ file for the specified font and magnification. Dvi2ln3 is making
+ a font load which replaces all the glyphs in that font with blanks
+ of the appropriate width. Check that the font file directories
+ TEX$LN03DIR or TEX$PXLDIR exist and are readable to you.
+
+Error while reading PXL file
+
+ There was some problem reading the PXL file for the specified
+ font and magnification. Perhaps the file is not in correct
+ format.
+
+No more room in LN03 font load buffer (xxx bytes long)
+
+ Dvi2ln3 is trying to build a font load, and the font load has
+ become so large that it has exceeded the size of a buffer in
+ the program. The probable cause of this is that your document
+ uses one or more very large fonts. Dvi2ln3 is not designed
+ to work with fonts larger than 36pt.
+
+-- Bad NFT file, lacks FONT.
+-- bad NFT file, not enough character parameters
+-- bad NFT file, bad character range.
+LN03 font file bad, using blanks
+
+ The LN03 font file found for the specified font has some
+ problem (probably it is not in the right format). Dvi2ln3
+ will build a font load in which the glyphs for that font
+ are replaced by appropriately sized blanks. Use NEWFFC to
+ examine the LN03 font file and track down what might be
+ wrong with it.
+
+Usage: Dvi2ln3 <filename> <options>
+
+ You have called dvi2ln3 without giving it a parameter
+ (the name of the file it has to process).
+
+Couldn't open dvi file
+Couldn't rewind dvi file.
+
+ Dvi2ln3 is having trouble accessing the dvi file you
+ have asked it to read. Check that the file exists and
+ that you have read access to it.
+
+ You could also get this message if you forgot the
+ space between the dvi file and the options on the
+ command line. E.g. if you typed dvi2ln3 foo/h=10
+ then dvi2ln3 would try to open a file named "foo/h=10"
+ and would obviously fail in the attempt.
+
+Bad DVI file - check it with DVItype
+
+ There is something wrong with the dvi file which Dvi2ln3
+ is trying to read. Run DVItype on that file to find out
+ what is wrong. Since TeX writes correct DVI files, it is
+ unlikely you will get this message...
+
+Couldn't open output file
+
+ Dvi2ln3 couldn't open the .ln3 file it is trying to
+ create. Perhaps you don't have write access in the
+ current directory; perhaps your disk quota is exceeded.
+
+Can't construct a font load:
+Dvi file uses too many glyphs from too many different fonts.
+
+ The LN03 has limits on how many glyphs and fonts it can
+ handle. Dvi2ln3 tries to make best use of the LN03 within
+ those limits. However, if you get the above message, it
+ means Dvi2ln3 has been unable to figure out how to print
+ your file within those limits. Solution: print your file
+ a few pages at a time using the /s and /n command line
+ options. If this problem occurs even when you try to print
+ just a single page, then you will have to simplify that page.
+
+Font xxx uses > 188 characters. Can't handle that.
+
+ Fonts could have as many as 256 characters. However, dvi2ln3
+ assumes you will only use 188 of them in any one document.
+ If you get tris message, try to print your file a few pages
+ at a time.
+
+Stack too deep for Dvi2ln3
+
+ The dvi file has a stack, and Dvi2ln3 is designed only
+ to allow that stack to go 100 entries deep. This is essentially a
+ design error in the program. Solution: recompile dvi2ln3 with
+ a larger STACKSIZE parameter.
+
+Too many fonts for Dvi2ln3
+
+ Dvi2ln3 is designed to handle only 100 fonts in your document.
+ Again this is essentially a desing error in the program.
+ Solution: recompile dvi2ln3 with a larger MAXTEXFONTS
+ parameter.
+
+Font xxx has a bad scaled size
+Font xxx has a bad design size
+
+ This indicates a bad DVI file format.
+
+Can't open TFM file for font xxx
+
+ If you use a font named foo, there has to exist a file
+ foo.tfm in tex$fonts. If you get the above message,
+ make sure that file exists and is readable to you.
+
+Bad TFM file for font xxx
+
+ The TFM file is in a bad format. Check it with
+ the TFtoPL program, which will tell you what is
+ wrong with it.
+
+\special too long (over xxx bytes).
+
+ \special's are limited to a certain length (2048 bytes
+ or something like that). There is no need for a \special
+ to be anything like that long, so if you get this
+ message, obviously you have coded the \special in
+ some weird way.
+
+Unrecognized option 'xxx' ignored in \special command
+
+ This means you have a \special with a command ln03:xxx
+ and xxx means nothing to dvi2ln3.
+
+Invalid point number (xxx)
+
+ The \special's that deal with points are only designed
+ to handle 255 of them, numbered 1 to 255. If you get
+ the above message, it means you have probably tried
+ to use a point number outside the range 1 to 255.
+
+Unable to open plotfile xxx
+
+ You used the ln03:plotfile \special, but dvi2ln3 was
+ incapable of reading the plotfile you told it to read.
+
+No such unit of measure: xxx
+
+ You have used xxx in a context in which \special syntax
+ is expecting a unit of measure (such as "in", "cm", "pt").
+
+Can't connect along a diagonal.
+
+ The ln03:connect \special can only connect two points which
+ are either at the same horizontal coordinate or at the
+ same vertical coordinate.
+
+
+ERRORS THAT DVI2LN3 DOES NOT CATCH. There are a number of
+possible errors in processing your document that Dvi2ln3
+does not catch. You have to detect them by looking at the
+printed output.
+
+These include:
+
+ errors in the use of TeX commands to achieve the formatting effects
+ you desire (of course);
+
+ misuse of the /h= and /v= command line options in such a way that
+ margins are too small or text material is set off the page;
+
+ errors in font files that cause rasters to be wrong (e.g. if
+ a font file has rasters for a "2" in the position where there ought
+ to be a "1");
+
+ gross discrepancies between the raster font files and the .tfm
+ files;
+
+ certain font file format errors in which a font file contains bad
+ pointers.
+
+In general, if any part of your document seems to look wrong,
+check that you are using TeX correctly, then check carefully
+which fonts are involved in the part that comes out wrong.
+
+
+9. PXL FILE CONVERSION
+
+If your PXL files are not in one of the acceptable record formats
+described in section 1, you will need to perform some conversion.
+There are many possibilities, so we can only suggest one approach
+that may be helpful. This approach seems to work if your PXL
+files were written by GFtoPXL.
+
+(a) Link the newffc program by saying:
+
+ $ link newffc,clink/opt
+
+or else
+
+ $ link newffc,sys$library:vaxcrtl/lib
+
+(b) Convert the resulting file to stream_LF as follows:
+
+ $ run newffc
+
+ FFC> rpxl foo.pxl
+ FFC> wstr foo.pxl
+ FFC> exit
+ $
+
+(c) You can also check out your PXL file using newffc.
+
+ $ run newffc
+
+ FFC> rpxl foo.pxl
+ FFC> txplr nn
+ [types rasters corresponding to character code nn on terminal]
+
+
+10. NEWFFC COMMANDS
+
+ EXIT
+ The obvious (control-Z also works)
+
+ RPXL
+ Read a PXL file into memory. The code tries to
+ accomodate a wide range of VMS file formats.
+
+ TPXLR <code>
+ Type the rasters for character code <code> from
+ the PXL file currently in memory
+
+ PPXLR <code>
+ Plot the rasters for PXL character <code>
+ using the "sixel" protocol accepted by
+ VT125, VT240 and VT241 terminals
+
+ TPXLL <offset>
+ Type the longword at offset <offset> in the
+ PXL file currently in memory
+
+ SPXLL <offset> <value>
+ Set the longword at offset <offset> to value
+ <value> in the PXL file currently in memory.
+ <value> is read in HEXADECIMAL (this being
+ considered the most convenient...).
+
+ WSTR <filename>
+ Write the PXL file currently in memory onto
+ the file <filename>, in stream_LF format as
+ desired by the LN03 and LaserWriter support.
+
+ INITNFT <code1> <code2>
+ Initialize the in-memory buffer for LN03 font files
+ with information appropriate to an LN03 file
+ accomodating characters from <code1> to <code2>.
+ Note: the only truly legal values are 33 and 126.
+
+ CPTONFT <code1> <count1>
+ Copy characters from the PXL file in memory to
+ the LN03 file in memory, beginning at <code1>
+ in the PXL file and going up to <code2>. Characters
+ are put at the lowest available codes in the
+ LN03 file.
+
+ RNFT <file>
+ Read unsixelized LN03 format font file into buffer
+
+ WNFT <filename>
+ Write the LN03 font file in memory to a file
+ <filename>. The file written out is not sixelized.
+
+ TNFTR <code>
+ Type the rasters for LN03 character, specified
+ as a decimal ascii code
+
+ PNFTR <code>
+ Plot the rasters for LN03 character <code>
+ using the "sixel" protocol accepted by
+ VT125, VT240 and VT241 terminals
+
+ TNFTL <offset>
+ Type longword of LN03 file buffer at given
+ byte <offset>
+
+ SNFTL <offset> <value>
+ Set longword of LN03 file buffer at given
+ byte <offset> to stated <value> in hex.
+
+
+
+11. BUG REPORTS
+
+Please report any bugs you may find. You may phone (617)
+467-7545, send Arpanet mail to Rose%dvinci.dec@decwrl.arpa, or
+Usenet mail to ...!decwrl!dec-rhea!dec-dvinci!rose. The best way
+to report a bug is to mail the TeX file that evokes it together
+with a description of what is wrong with the printed output.
+
+If you should wish to port this software to operating systems
+other than VAX/VMS, please do not hesitate to contact me.
+
+
+12. UNIX
+
+The sources for dvi2ln3 and newffc may be recompiled under
+Unix on a VAX, and will probably run there. This has been
+tried and tested (to a limited extent) under Ultrix-32 and
+4.3bsd.
+
+When running under Unix, Dvi2ln3 expects three "environment
+variables" to be defined:
+
+ TEX_FONTS wherever the tfms are
+ TEX_LN03DIR wherever the LN03 format font files are
+ TEX_PXLDIR wherever the PXL format files are
+
+Define these with setenv.
+
+You may need to modify dvi2ln3 and newffc to run on your
+Unix system. In particular, if your Unix runs on a 68000 or
+PC RT or something like that, you may have to make serious
+modifications. Look in particular at all C "union"
+constructs in the source; the code that manipulates them may
+need to be redefined because of the machine architecture
+(this is necessary, e.g., on a 68000).
diff --git a/dviware/ln03/rose/ln03topp.pli b/dviware/ln03/rose/ln03topp.pli
new file mode 100644
index 0000000000..30ff2bf13d
--- /dev/null
+++ b/dviware/ln03/rose/ln03topp.pli
@@ -0,0 +1,1255 @@
+/*
+
+This program is LN03Topp, which converts the output of DVI2LNG to LN03
+format files. Copyright (c) 1984 by Digital Equipment Corporation. Author:
+Flavio Rose, UHCLEM::ROSE.
+
+This program is UNDER DEVELOPMENT...
+
+Development chronology:
+
+11/26/84: Started coding. Changed filenames.
+11/28/84: New algorithm for allocation of TeX fonts to LN03 font slots
+11/29/84: Extensive changes in TOPPNFT. NBLANKS reduced to 0, perhaps
+ temporarily, to simplify font stuff.
+11/30/84: Fixed opening escape sequences.
+5/6/85: Corrected bug in placement of comma "," inside font load
+5/18/85: Corrected longstanding bugs, which also existed in Topp:
+ xpos and ypos are initialized to 0 rather than to dxof,
+ dyof; and their are not reset on a NEWPAGE.
+
+
+*/
+
+%REPLACE false BY '0'B;
+%REPLACE true BY '1'B;
+
+ln03topp: PROCEDURE OPTIONS(MAIN);
+
+ DCL (strip,frontstrip) EXTERNAL ENTRY (CHAR (*))
+ RETURNS (CHAR (*)),
+ cvis EXTERNAL ENTRY (FIXED) RETURNS (CHAR (*));
+ DCL upcase EXTERNAL ENTRY (CHAR (*)) RETURNS (CHAR (*));
+ DCL add_txf_to_lnf EXTERNAL ENTRY(FIXED,FIXED,CHAR(*),FILE)
+ RETURNS(FIXED);
+
+/* Debugging info */
+
+ DCL debug FILE;
+ DCL debugwid FIXED;
+ DCL debugflag BIT;
+ debugflag = false;
+ debugwid = 0;
+
+/* input file, output file, include file */
+
+ DCL (outf,inf,incfile) FILE;
+ DCL (infnam,outfnam) CHAR(150) VARYING;
+ DCL reading_from_incfile BIT;
+ reading_from_incfile = false;
+
+ ON UNDEFINEDFILE (inf) BEGIN;
+ PUT SKIP LIST('No such file: ' || infnam);
+ GOTO terminal_eof;
+ END;
+
+ ON UNDEFINEDFILE (outf) BEGIN;
+ PUT SKIP LIST('Couldn''t open: ' || outfnam);
+ GOTO terminal_eof;
+ END;
+
+ ON ENDFILE (inf) BEGIN;
+ CLOSE FILE(inf);
+ GOTO end_of_input;
+ END;
+
+/* Define <esc> <tab> as constants. In general this source file should not
+contain any ascii control characters; instead we use byte(). */
+
+ DCL (esc,tab) CHAR(1);
+ esc = byte(27);
+ tab = byte(9);
+
+/* Banner string */
+
+ PUT SKIP LIST ('LN03Topp 2');
+
+/* Get input and output files from command line. */
+
+ %INCLUDE $stsdef;
+ DCL lib$get_foreign ENTRY (CHAR(*) VARYING, CHAR(*) VARYING,
+ FIXED(15),FIXED) OPTIONS(VARIABLE) RETURNS(FIXED);
+ DCL indir CHAR(150) VARYING;
+ DCL (jj,jnam,jext,dxof,dyof) FIXED;
+
+/* Unlike Topp, this driver conforms to the TeX requirement that drivers
+imaging onto 8 1/2 x 11 inch paper place the (0,0) point of the DVI file
+one inch horizontally and vertically offset from the upper left corner of
+the paper. Hence we initialize the following offset variables, which may be
+reset by command line options. */
+
+ dxof = 300;
+ dyof = 300;
+
+/* ilp = input line ptr, illen = input line length, incsave = place to save
+rest of line when {INCLUDE} is encountered. */
+
+ %REPLACE inbufsize BY 2048;
+ DCL inline CHAR(inbufsize) VARYING;
+ DCL incsave CHAR(inbufsize) VARYING;
+ DCL tch CHAR(1);
+ DCL (illen,ilp,i,istart,istart2,zch) FIXED;
+
+ sts$value = lib$get_foreign(inline,,,0);
+
+ IF ^sts$success THEN DO;
+ PUT SKIP EDIT ('Usage: ln03topp <filename> <options>') (A);
+ GOTO terminal_eof;
+ END;
+
+ inline = inline || ' ';
+ illen = length(inline);
+ ilp = 1;
+ CALL skipb;
+ IF ilp > illen THEN DO;
+ PUT SKIP EDIT ('Usage: ln03topp <filename> <options>') (A);
+ GOTO terminal_eof;
+ END;
+ istart = ilp;
+ CALL skipnb;
+ infnam = substr(inline,istart,ilp-istart);
+ OPEN FILE(inf) TITLE(infnam)
+ ENVIRONMENT(Default_file_name('FOO.LNG'));
+
+ jnam = istart;
+ DO jj = ilp-1 TO istart BY -1;
+ IF substr(inline,jj,1) = ':' |
+ substr(inline,jj,1) = ']' |
+ substr(inline,jj,1) = '>'
+ THEN DO;
+ jnam = jj+1;
+ GOTO found_jnam;
+ END;
+ END;
+
+found_jnam:
+
+/* We need to remember the directory of the input filespec, so that we can
+open {INCLUDE} files in the same directory. Thus, we stash that directory
+in INDIR. */
+
+ indir = substr(inline,istart,jnam-istart);
+ jext = ilp;
+ DO jj = jnam TO ilp-1;
+ IF substr(inline,jj,1) = '.' |
+ substr(inline,jj,1) = ';'
+ THEN DO;
+ jext = jj;
+ GOTO found_jext;
+ END;
+ END;
+
+found_jext:
+
+/* The output file is placed in the current default directory; i.e., we get
+rid of the directory part and just use the name part of the input filespec.
+*/
+
+ outfnam = substr(inline,jnam,jext-jnam) || '.LN3';
+
+ OPEN FILE(outf) OUTPUT TITLE(outfnam)
+ ENVIRONMENT(Default_file_name('FOO.LN3'));
+
+/* Read any options from the command line. There are two possible options,
+/H= and /V=, which serve to locate the (0,0) point of the DVI file on the
+paper. */
+
+ CALL skipb;
+ DO WHILE (ilp <= illen-2);
+ IF substr(inline,ilp,3) = '/H=' THEN DO;
+ ilp = ilp+3;
+ jj = -1;
+ CALL getfixnum(jj);
+ dxof = jj;
+ END; ELSE IF substr(inline,ilp,3) = '/V=' THEN DO;
+ jj = -1;
+ ilp = ilp+3;
+ CALL getfixnum(jj);
+ dyof = jj;
+ END; ELSE CALL skipnb;
+ CALL skipb;
+ END;
+
+/* Globals: there are many of these
+
+ font information */
+
+ %REPLACE maxnfonts BY 21; /* so I am told */
+ %REPLACE maxtexfonts BY 100;
+ %REPLACE maxfontnos BY 9;
+ %REPLACE nblanks BY 0;
+ DCL (curf,fsize,totsize,chunkcnt) FIXED;
+ DCL curtxf PTR;
+ DCL lastch(0:maxnfonts) FIXED GLOBALDEF;
+ DCL chw(0:maxnfonts,0:255) FIXED GLOBALDEF;
+ DCL ftime(0:maxnfonts) FIXED;
+
+ fsize = 0;
+ totsize = 0;
+ curf = 0;
+ curtxf = Null();
+ chw = 0;
+ ftime = 0;
+ chunkcnt = 0;
+
+/* An LN03 "font" actually consists of two LN03 font loads, one to be
+placed in character positions 33 to 126, the other in positions 161 to 254.
+There are no raster size limitations on LN03 fonts, other than those
+imposed by this program (those should eventually be removed). */
+
+ %REPLACE leftfirst BY 33;
+ %REPLACE rightfirst BY 161;
+ %REPLACE leftlast BY 126;
+ %REPLACE rightlast BY 254;
+
+/* TXF is a record structure describing a TeX font. TXFA is an array of TXF
+records. TXF2LNF describes the mapping between TeX fonts and LN03 "fonts".
+*/
+
+ DCL 1 txf BASED,
+ 2 nchs FIXED,
+ 2 chu(0:255) CHAR(1);
+ DCL txfa(0:maxtexfonts) PTR GLOBALDEF;
+ DCL txf2lnf(0:maxtexfonts) FIXED;
+
+ txfa = Null();
+ txf2lnf = -1;
+
+/* WHBLANKS identifies the locations of the fixed width blanks in the LN03
+fonts. FNAME is the font file ID of each LN03 font. For the moment,
+however, we do not use WHBLANKS. */
+
+/* DCL whblanks(0:maxnfonts,1:nblanks) FIXED GLOBALDEF; */
+ DCL fname(0:maxnfonts) CHAR(31) GLOBALDEF;
+
+/* whblanks = -1;*/
+ fname = 'U000000002SK00GG0001UZZZZ02F000';
+ DO i = 0 TO MIN(maxnfonts,9);
+ substr(fname(i),7,1) = cvis(i);
+ END;
+ DO i = 10 TO maxnfonts;
+ substr(fname(i),6,2) = cvis(i);
+ END;
+
+/* The LN03 select-graphic-rendition numbers 0-9 have to be allocated among
+the fonts. The USENO array keeps track of which number a font is allowed to
+use, -1 if none is currently allocated. The WHOUSES array says which font
+is using a number. */
+
+ DCL (useno(0:maxnfonts),whouses(0:maxfontnos)) FIXED;
+
+ useno = -1;
+ whouses = -1;
+
+/* The landscape bit is for future reference; currently LN03Topp does not
+support landscape printing */
+
+ DCL landscape BIT;
+
+ landscape = false;
+
+ DCL (maxrmar,maxbmar) FIXED;
+ maxrmar = 2550;
+ maxbmar = 3300;
+
+/* In the following, esc[?27h means "advance the carriage by the character
+width when you set a character", esc[11h and esc[7 I together mean to
+interpret all dimensions in escape sequences as pixel units; esc[?52h means
+our origin of coordinates is the upper left edge of the paper; esc[3300t
+means the "maximum length" of the paper is 3300 pixels. */
+
+ PUT EDIT (esc || 'c') (A) FILE(outf);
+ PUT EDIT (esc || '[?27h' || esc || '[11h' || esc || '[7 I'
+ || esc || '[?52h' || esc || '[3300t')
+ (A) FILE(outf);
+
+/* A count of pages is plotted out on the terminal to give users an
+indication of progress. */
+
+ DCL pageno FIXED;
+ pageno = 1;
+
+/* Current x and y positions: always in pixels. */
+
+ DCL (xpos,ypos) FIXED;
+ xpos = dxof;
+ ypos = dyof;
+
+/* User definable command area: This is left over from the LNF program
+whence Topp and then LN03Topp are derived. This stuff should be taken out.
+*/
+
+ %REPLACE udcsize BY 4096;
+ DCL 1 udcr BASED,
+ 2 (udcname,udcnamelen,udcvalue,udcvaluelen) FIXED,
+ 2 next PTR;
+ DCL udclist PTR;
+ DCL udcarea CHAR(udcsize);
+ DCL udcarray(udcsize) CHAR(1) BASED (addr(udcarea));
+ DCL udcaptr FIXED;
+
+ udclist = Null();
+ udcaptr = 1;
+ /* Predefine some commands */
+ IF define_command('LB','{CHAR123}') THEN;
+ IF define_command('RB','{CHAR125}') THEN;
+
+/* Page storage: we save up a page before outputting it to minimize the
+complexity of the page for the LN03. Since the capacity limitations of the
+LN03, unlike those of the LN01, are nowhere defined, it is unclear whether
+the transformations we perform are actually useful. */
+
+/* The page is stored as a set of characters in the PBUF, plus a collection
+of lists of "chunks". The lists are indexed by Y-position, using the array
+CHLS. Each chunk is a sequence of characters that can be written without
+backspacing or changing vertical position. */
+
+ %REPLACE pbufsize BY 65536;
+ DCL pbuf(0:pbufsize) CHAR(1);
+ DCL chls(0:3300) PTR; /* 3300 is maximum value of maxbmar */
+
+/* The CHUNK record keeps track of a chunk, giving the X position at which
+it starts and ends (XS, XE), the index of where its characters start and
+end in PBUF (IS, IE), plus the TeX font in which it is set (FS). */
+
+ DCL 1 chunk BASED,
+ 2 (xs,xe,is,ie,fs) FIXED,
+ 2 next PTR;
+
+/* Chunk records are allocated from a free list. PBUFI keeps track of how
+much of PBUF is being used. CHUNKMAX is a count of how many chunks are
+allocated (not used for anything). */
+
+ DCL (free_chunk_list,curchunk) PTR;
+ DCL (pbufi,chunkmax) FIXED;
+ DCL top_of_page BIT;
+
+ pbufi = 0;
+ chunkmax = 0;
+ free_chunk_list = Null();
+ chls = Null();
+ curchunk = Null();
+ top_of_page = false;
+
+/* This is the main loop: */
+
+ DO WHILE (true);
+ CALL read_inline;
+
+ illen = length(inline);
+ ilp = 1;
+ istart2 = 1;
+
+ DO WHILE (ilp <= illen);
+ tch = substr(inline,ilp,1);
+ IF tch = '{' THEN DO;
+ IF istart2 < ilp THEN CALL ord_text(substr(inline,
+ istart2,ilp-istart2));
+ CALL handle_command;
+ istart2 = ilp;
+ END; ELSE ilp = ilp+1;
+ END;
+ IF istart2 < ilp THEN CALL ord_text(substr(inline,istart2,ilp-
+ istart2));
+ END;
+
+end_of_input:
+ ;
+
+abort_job:
+ PUT EDIT (esc || 'c') (A) FILE(outf);
+ CLOSE FILE(outf);
+
+terminal_eof:
+ RETURN;
+
+/* Decipher command from inline and then execute it */
+
+ handle_command: PROCEDURE;
+ DCL (j,k,fno) FIXED;
+ DCL (command,name) CHAR(16) VARYING;
+
+ ON UNDEFINEDFILE (incfile) BEGIN;
+ PUT SKIP LIST('No such file:' || substr(inline,istart,
+ ilp-istart));
+ GOTO windup_command;
+ END;
+
+ istart = ilp+1;
+ ilp = istart;
+ DO WHILE ('A' <= substr(inline,ilp,1) &
+ substr(inline,ilp,1) <= 'Z');
+ ilp = ilp+1;
+ END;
+ command = substr(inline,istart,ilp-istart);
+ IF command = 'HPA' | command = 'X' THEN DO;
+ k = -1;
+ CALL getfixnum(k);
+ k = k+dxof;
+ xpos = k;
+ END; ELSE IF command = 'VPA' THEN DO;
+ j = 0;
+ CALL getfixnum(j);
+ j = j+dyof;
+ CALL finish_chunk;
+ ypos = j;
+ END; ELSE IF command = 'NOFILL' THEN;
+ /* ignore, always in nofill mode */
+ ELSE IF command = 'DEBUG' THEN debugflag = true;
+ ELSE IF command = 'WLN' THEN CALL process_wln;
+ ELSE IF command = 'NODEBUG' THEN debugflag = false;
+ ELSE IF command = '' THEN DO;
+ CALL findcb2;
+ ilp = ilp+1;
+ END; ELSE IF command = 'NEWPAGE' THEN DO;
+ CALL finish_chunk;
+ CALL finish_page;
+ xpos = dxof;
+ ypos = dyof;
+ END; ELSE IF command = 'FO' THEN DO;
+ CALL getfixnum(fno);
+ IF fno < 0 | fno > maxtexfonts THEN CALL error1
+ ('Bad font number ' || cvis(fno) || ' in FO command.');
+ IF txfa(fno) = Null() THEN CALL error1
+ ('Font number ' || cvis(fno) || ' not present.');
+ CALL finish_chunk;
+ curf = fno;
+ curtxf = txfa(fno);
+ END; ELSE IF command = 'INCLUDE' THEN DO;
+ IF reading_from_incfile THEN CALL
+ error1('{INCLUDE}s can''t be nested.');
+ CALL skipb;
+ istart = ilp;
+ CALL skipnb2;
+ OPEN FILE (incfile) TITLE (substr(inline,istart,ilp-istart))
+ ENVIRONMENT(Default_file_name(indir || 'FOO.LNG'));
+ reading_from_incfile = true;
+ CALL findcb;
+ ilp = ilp+1;
+ IF rest_of_inline_blank() THEN incsave = '';
+ ELSE incsave = substr(inline,ilp,illen-ilp);
+ ilp = illen+1;
+ END; ELSE IF command = 'LMAR' | command = 'RMAR' |
+ command = 'TMAR' | command = 'BMAR' THEN;
+ ELSE IF command = 'RULEA' THEN DO;
+ DCL (x0,y0,x1,y1,temp) FIXED;
+
+ CALL getfixnum(x0);
+ CALL getfixnum(y0);
+ CALL getfixnum(x1);
+ CALL getfixnum(y1);
+ x0 = min(x0+dxof,maxrmar);
+ x1 = min(x1+dxof,maxrmar);
+ x0 = max(x0,0);
+ x1 = max(x1,0);
+ y0 = min(y0+dyof,maxbmar);
+ y1 = min(y1+dyof,maxbmar);
+ y0 = max(y0,0);
+ y1 = max(y1,0);
+ IF x0 > x1 THEN DO; temp = x0; x0 = x1; x1 = temp; END;
+ IF y0 > y1 THEN DO; temp = y0; y0 = y1; y1 = temp; END;
+
+ IF top_of_page THEN DO;
+ top_of_page = false;
+ PUT EDIT (byte(12)) (A) FILE(outf);
+ END;
+
+/* All rules are put at the beginning of the page, a separate line for each
+one. A vertical positioning command is necessary because otherwise the LN03
+would keep on advancing the vertical position as more rules are set, in
+response to the end of line carriage returns. */
+
+ IF y1 ^= y0 & x1 ^= x0 THEN DO;
+ IF landscape
+ THEN PUT SKIP EDIT (esc || '[100d' ||
+ esc || '[0;' || cvis(y0) || ';' ||
+ cvis(x0) || ';' || cvis(y1-y0) || ';' ||
+ cvis(x1-x0) || '!|') (A) FILE(outf);
+ ELSE PUT SKIP EDIT (esc || '[100d' ||
+ esc || '[1;' || cvis(x0) || ';' ||
+ cvis(y0) || ';' || cvis(y1-y0) || ';' ||
+ cvis(x1-x0) || '!|') (A) FILE(outf);
+ END;
+
+ END; ELSE IF command = 'CHAR' THEN DO;
+ j = 0;
+ CALL getfixnum(j);
+ CALL ord_text(byte(j));
+ END; ELSE IF command = 'CU' THEN DO;
+ CALL getfixnum(fno);
+ IF fno < 0 | fno > maxtexfonts THEN CALL error1
+ ('Bad font number ' || cvis(fno) || ' in CU command.');
+ CALL skipb;
+ IF txfa(fno) = Null() THEN
+ ALLOCATE txf SET (txfa(fno));
+ txfa(fno) -> chu = byte(0);
+ j = 0;
+ k = 0;
+ DO WHILE (substr(inline,ilp,1) ^= '}' & substr(inline,ilp,1)
+ ^= ' ');
+ IF substr(inline,ilp,1) = '1' THEN DO;
+ txfa(fno) -> chu(j) = byte(1);
+ k = k+1;
+ END;
+ j = j+1;
+ ilp = ilp+1;
+ END;
+ txfa(fno) -> txf.nchs = k;
+
+ END; ELSE IF command = 'DEF' THEN DO;
+ /* Definition of a user defined command : very delicate */
+
+ CALL skipb;
+ IF ilp > illen THEN CALL error1('DEF has no parameters.');
+ IF substr(inline,ilp,1) ^= '{' THEN DO;
+ CALL findcb2;
+ CALL error1('DEF syntax is {DEF{command}value}.');
+ END;
+ ilp = ilp+1;
+ istart = ilp;
+ DO WHILE ('A' <= substr(inline,ilp,1) & substr(inline,ilp,1)
+ <= 'Z');
+ ilp = ilp+1;
+ END;
+ IF substr(inline,ilp,1) ^= '}' THEN DO;
+ CALL findcb;
+ ilp = ilp+1;
+ CALL findcb2;
+ CALL error1('DEF syntax is {DEF{command}value}.');
+ END;
+ IF ilp = istart | ilp-istart > 16 THEN CALL error1
+ ('DEF ignored, new command name blank or too long.');
+ k = ilp;
+ ilp = ilp+1;
+ CALL findcb2;
+ IF ^define_command(substr(inline,istart,k-istart),
+ substr(inline,k+1,ilp-k-1))
+ THEN CALL bad_error('User command area exhausted.');
+ END; ELSE IF command ^= '' THEN DO;
+ DCL p PTR;
+
+ DO p = udclist REPEAT p -> udcr.next WHILE (p ^= Null());
+ IF command = substr(udcarea,p -> udcname,p -> udcnamelen)
+ THEN DO;
+ CALL findcb;
+ ilp = ilp+1;
+ IF p -> udcvaluelen + illen-ilp+1 > inbufsize THEN
+ CALL error1 ('Input buffer size exceeded, ' ||
+ 'user command ' || command || ' ignored.');
+ /* Put Udc in stream to be executed */
+ inline = substr(udcarea,p -> udcvalue,
+ p -> udcvaluelen) || substr(inline,ilp,
+ illen-ilp+1);
+ illen = length(inline);
+ ilp = 1;
+ RETURN;
+ END;
+ END;
+ CALL error1 ('No such command: ' || command);
+ END;
+
+ windup_command:
+ CALL findcb;
+ ilp = ilp+1;
+ RETURN;
+
+ findcb2: PROCEDURE;
+ DCL cbc FIXED;
+
+ cbc = 0;
+ DO WHILE (ilp <= illen);
+ IF substr(inline,ilp,1) = '{' THEN cbc = cbc+1;
+ ELSE IF substr(inline,ilp,1) = '}' THEN cbc = cbc-1;
+ IF cbc < 0 THEN RETURN;
+ ilp = ilp+1;
+ END;
+
+ END findcb2;
+
+ error1: PROCEDURE(str);
+ DCL str CHAR(*);
+
+ PUT SKIP EDIT (str) (A);
+ GOTO windup_command;
+
+ END error1;
+
+ bad_error: PROCEDURE(str);
+ DCL str CHAR(*);
+
+ PUT SKIP EDIT (str) (A);
+ GOTO abort_job;
+
+ END bad_error;
+
+ END handle_command;
+
+
+ finish_chunk: PROCEDURE;
+
+ curchunk = Null();
+
+ END finish_chunk;
+
+/* skip over blanks in inline */
+
+ skipb: PROCEDURE;
+
+ DO WHILE (ilp <= illen);
+ IF substr(inline,ilp,1) ^= ' ' THEN RETURN;
+ ilp = ilp+1;
+ END;
+
+ END skipb;
+
+/* skip over characters other than ' ' and '}'. */
+
+ skipnb2: PROCEDURE;
+
+ DO WHILE (ilp <= illen);
+ IF substr(inline,ilp,1) = ' '
+ | substr(inline,ilp,1) = '}' THEN RETURN;
+ ilp = ilp+1;
+ END;
+
+ END skipnb2;
+
+ skipnb: PROCEDURE;
+
+ DO WHILE (ilp <= illen);
+ IF substr(inline,ilp,1) = ' ' THEN RETURN;
+ ilp = ilp+1;
+ END;
+
+ END skipnb;
+
+/* Read a fixnum from inline and put it in x. If can't find one then x is
+left unchanged. */
+
+ getfixnum: PROCEDURE (x);
+ DCL (x,x1) FIXED;
+ DCL negative BIT;
+
+ negative = false;
+ IF ilp > illen THEN RETURN;
+ CALL skipb;
+ IF substr(inline,ilp,1) = '-' THEN DO;
+ negative = true;
+ ilp = ilp+1;
+ END;
+
+ IF ilp > illen | substr(inline,ilp,1) < '0'
+ | substr(inline,ilp,1) > '9'
+ THEN RETURN;
+
+ x1 = 0;
+ DO WHILE(substr(inline,ilp,1) >= '0' &
+ substr(inline,ilp,1) <= '9'); /* cheat */
+ x1 = 10*x1 + RANK(substr(inline,ilp,1)) - RANK('0');
+ ilp = ilp+1;
+ IF ilp > illen THEN GOTO gotnum;
+ END;
+
+ gotnum:
+ x = x1;
+ IF negative THEN x = -x;
+ RETURN;
+
+ END getfixnum;
+
+/* find close bracket } */
+
+ findcb: PROCEDURE;
+
+ IF ilp >= illen THEN RETURN;
+ DO WHILE (ilp < illen & substr(inline,ilp,1) ^= '}');
+ ilp = ilp+1;
+ END;
+
+ END findcb;
+
+/* determine if rest of inline is blank */
+
+ rest_of_inline_blank: PROCEDURE RETURNS (BIT);
+ DCL j FIXED;
+
+ IF ilp > illen THEN RETURN (true);
+ j = ilp;
+ DO WHILE (j <= illen);
+ IF (substr(inline,j,1) ^= ' '
+ & substr(inline,j,1) ^= tab) THEN RETURN(false);
+ j = j+1;
+ END;
+ RETURN(true);
+
+ END rest_of_inline_blank;
+
+/* The following procedure is still called 'process_wln', even though the
+file extensions now default to '.nft' rather than '.tln'. Magnified font
+extensions still have to be numeric... */
+
+ process_wln: PROCEDURE;
+ DCL (j,k,l,fno,maxfno,chsize) FIXED;
+ DCL font_selected BIT;
+ DCL (txfns(0:maxtexfonts),txfne(0:maxtexfonts)) FIXED;
+
+/* Destroy all traces of preceding fonts */
+
+ totsize = 0;
+ chsize = 0;
+/* whblanks = -1;*/
+ useno = -1;
+ whouses = -1;
+ txfns = -1;
+ txfne = -1;
+ maxfno = 0;
+ PUT SKIP EDIT(esc || 'P0;1;1y') (A) FILE(outf);
+
+/* Read the font names and numbers from inline. Because LN03 fontnames can
+be very long, this will probably have to be changed to read from more than
+one line. For 100 fonts, 2048 characters in the line is not enough. */
+
+ CALL skipb;
+
+ DO WHILE (substr(inline,ilp,1) ^= '}' & ilp < illen);
+ istart = ilp;
+ CALL skipnb2;
+ j = ilp;
+ fno = 0;
+ CALL getfixnum(fno);
+ IF fno < 0 | fno > maxtexfonts THEN DO;
+ PUT SKIP EDIT ('Bad font no.: ' || cvis(fno)) (A);
+ GOTO abort_job;
+ END;
+ txfns(fno) = istart;
+ txfne(fno) = j;
+ IF fno > maxfno THEN maxfno = fno;
+ IF txfa(fno) = Null() THEN DO;
+ ALLOCATE txf SET(txfa(fno));
+ DO k = 0 TO 127;
+ txfa(fno) -> chu(k) = byte(1);
+ END;
+ DO k = 128 TO 255;
+ txfa(fno) -> chu(k) = byte(0);
+ END;
+
+/* The default assumption, if the input contains no CU command for a given
+font, is that only characters 0-127 are used. For efficiency's sake this is
+not actually checked as the characters are read in and translated. */
+
+ txfa(fno) -> nchs = 128;
+ END;
+ IF txfa(fno) -> nchs > 182 THEN GOTO need_empty_slots;
+ chsize = chsize + txfa(fno) -> nchs;
+ CALL skipb;
+ END;
+
+/* Now we have to allocate TeX fonts (TXFs) to LN03 fonts (LNFs) and
+perform the actual load. The goal is to use as few slots as possible, where
+an LNF with <= 94 characters is one slot, and an LNF with > 94 but <= 188
+characters is two slots.
+
+As in the LN01 case, the allocation problem seems to be some sort of hard
+bin packing problem. (Proof of NP-hardness, anyone?) So we use a best fit
+heuristic:
+
+1. Find the unassigned largest TXF. If none, exit.
+
+2. Allocate an LNF for it.
+
+3. Find the largest TXF that fits in what is left of that LNF.
+
+ 3.1. If none exists, go to 1.
+ 3.2. If one exists, put it in the LNF and go back to 3.
+
+The TXFORD array will contain the TXF numbers in the order that they are
+assigned. TXFCNT keeps track of how many TXFs have been assigned so far. */
+
+ DCL (lnfcnt,txfcnt,the_txf,txf_size,lnfleft) FIXED;
+ DCL txford(0:maxtexfonts) FIXED;
+
+ lnfcnt = 0;
+ txfcnt = 0;
+ txford = -1;
+
+
+
+ DO WHILE (true);
+
+/* Find largest unassigned TXF */
+
+ lnfleft = rightlast-rightfirst+1+leftlast-leftfirst+1-nblanks;
+ txf_size = -1;
+ DO j = 0 TO maxtexfonts;
+ IF txfa(j) ^= Null() & txf2lnf(j) = -1
+ & txfa(j) -> nchs > txf_size THEN DO;
+ txf_size = txfa(j) -> nchs;
+ the_txf = j;
+ END;
+ END;
+ IF txf_size <= 0 THEN GOTO assignment_done;
+ IF lnfcnt > maxnfonts THEN GOTO too_complex;
+ IF txf_size > lnfleft THEN GOTO need_empty_slots;
+
+/* Now allocate new LNF and put TXF in it */
+
+ txf2lnf(the_txf) = lnfcnt;
+ txford(txfcnt) = the_txf;
+ txfcnt = txfcnt+1;
+ k = leftfirst-1;
+ DO j = 0 TO 255;
+ IF txfa(the_txf) -> chu(j) ^= byte(0) THEN DO;
+ k = k+1;
+ IF k = leftlast+1 THEN k = rightfirst;
+ lnfleft = lnfleft-1;
+ txfa(the_txf) -> chu(j) = byte(k);
+ END;
+ END;
+ lastch(lnfcnt) = k;
+
+/* Now try to fill the remaining part of the LNF using other TeX fonts */
+
+ DO WHILE (true);
+
+ txf_size = -1;
+ DO j = 0 TO maxtexfonts;
+ IF txfa(j) ^= Null() & txf2lnf(j) = -1
+ & txfa(j) -> nchs > txf_size
+ & txfa(j) -> nchs <= lnfleft THEN DO;
+ txf_size = txfa(j) -> nchs;
+ the_txf = j;
+ END;
+ END;
+ IF txf_size <= 0 THEN GOTO current_lnf_exhausted;
+
+ txf2lnf(the_txf) = lnfcnt;
+ txford(txfcnt) = the_txf;
+ txfcnt = txfcnt+1;
+ k = lastch(lnfcnt);
+ DO j = 0 TO 255;
+ IF txfa(the_txf) -> chu(j) ^= byte(0) THEN DO;
+ k = k+1;
+ lnfleft = lnfleft-1;
+ IF k = leftlast+1 THEN k = rightfirst;
+ txfa(the_txf) -> chu(j) = byte(k);
+ END;
+ END;
+ lastch(lnfcnt) = k;
+ END;
+
+ current_lnf_exhausted:
+ lnfcnt = lnfcnt+1;
+ END;
+
+assignment_done:
+
+/* At this point the array TXF2LNF is properly initialized, as are LASTCH
+and TXFORD. It remains to actually generate the desired font load. This has
+to be done carefully, since characters may only be added to an LNF in
+ascending order. */
+
+ DO j = 0 TO txfcnt-1;
+ k = txford(j);
+ IF j ^= 0 & txf2lnf(k) ^= txf2lnf(txford(j-1)) THEN PUT SKIP
+ EDIT (',') (A) FILE(outf);
+ totsize = totsize+add_txf_to_lnf(txf2lnf(k),k,
+ substr(inline,txfns(k),
+ txfne(k)-txfns(k)),outf);
+ END;
+
+/* At this point we need to write a message saying how long the font load
+is overall. That comes from TOTSIZE plus an overhead factor that depends on
+how many LNFs there are and whether any of them don't use the right half of
+the code chart. */
+
+ PUT SKIP EDIT (';LN03Topp 2 font load' || esc || '\') (A) FILE(outf);
+ IF landscape THEN DO;
+ maxrmar = 3300;
+ maxbmar = 2550;
+/* PUT EDIT(esc || 'c') (A) FILE(outf);*/
+ PUT EDIT (esc || '[1;3300s' || esc || '[2550t' ||
+ esc || '[1;2550r')
+ (A) FILE(outf);
+ END; ELSE DO;
+ maxbmar = 3300;
+ maxrmar = 2550;
+/* PUT EDIT(esc || 'c') (A) FILE(outf);*/
+ PUT EDIT(esc || '[1;2550s' || esc || '[3300t' ||
+ esc || '[1;3300r')
+ (A) FILE(outf);
+ END;
+ font_selected = false;
+ DO j = 0 TO MIN(maxfontnos,lnfcnt-1);
+ useno(j) = j;
+ whouses(j) = j;
+ PUT EDIT
+ (esc || 'P1;1' || cvis(j) || '}' ||
+ substr(fname(j),1,16) || esc || '\') (A)
+ FILE(outf);
+ IF ^font_selected THEN DO;
+ PUT EDIT (esc || '[1' || cvis(j) || 'm') (A)
+ FILE(outf);
+ font_selected = true;
+ END;
+ END;
+
+ RETURN;
+
+ too_complex:
+ PUT SKIP EDIT ('Can''t construct usable font load: input ' ||
+ 'too complex') (A);
+ GOTO abort_job;
+
+ need_empty_slots:
+ PUT SKIP EDIT ('Font ' || cvis(fno) || ' uses >182 characters.'
+ || ' LN03Topp can''t handle that.') (A);
+ GOTO abort_job;
+
+ rasters_too_large:
+ PUT SKIP EDIT ('The required rasters from font ' || cvis(fno) ||
+ ' are too large for this preliminary LN03Topp to handle.') (A);
+ GOTO abort_job;
+
+ END process_wln;
+
+ /* insert ordinary text into page */
+ ord_text: PROCEDURE (c);
+ DCL c CHAR(*);
+ DCL (c1,c2) CHAR(1);
+ DCL (t,j,zch) FIXED;
+
+ t = length(c);
+ IF t = 0 THEN RETURN;
+ IF xpos < 0 | xpos > maxrmar | ypos < 0 | ypos > 3300 THEN DO;
+ PUT SKIP EDIT ('"') (A);
+ DO j = 1 TO t;
+ IF substr(c,j,1) >= ' ' THEN PUT EDIT
+ (substr(c,j,1)) (A);
+ ELSE PUT EDIT ('.') (A);
+ END;
+ PUT EDIT ('" set off paper ') (A);
+ IF xpos < 0 | xpos > maxrmar THEN PUT EDIT ('(X = ' || cvis(xpos)
+ || ') ') (A);
+ IF ypos < 0 | ypos > 3300 THEN PUT EDIT ('(Y = ' || cvis(ypos)
+ || ') ') (A);
+ IF xpos < 0 THEN xpos = 0; ELSE xpos = maxrmar;
+ IF ypos < 0 THEN ypos = 0; ELSE ypos = 3300;
+ END;
+ IF curchunk = Null() THEN DO;
+ CALL new_chunk;
+ curchunk -> chunk.xs = xpos;
+ curchunk -> chunk.xe = xpos;
+ curchunk -> chunk.is = pbufi;
+ END;
+ IF xpos < curchunk -> chunk.xe THEN DO;
+ CALL finish_chunk;
+ CALL new_chunk;
+ curchunk -> chunk.xs = xpos;
+ curchunk -> chunk.is = pbufi;
+ END; ELSE IF xpos > curchunk -> chunk.xe
+ THEN CALL move_forward(xpos-curchunk -> chunk.xe);
+ IF pbufi+t >= pbufsize THEN DO;
+ PUT SKIP EDIT('Topp capacity exceeded: ' ||
+ 'too much stuff on one page.') (A);
+ GOTO abort_job;
+ END;
+ DO j = 1 TO t;
+ c2 = substr(c,j,1);
+ zch = rank(c2);
+ c1 = curtxf -> chu(zch);
+ zch = rank(c1);
+ pbuf(pbufi+j-1) = c1;
+ xpos = xpos+chw(txf2lnf(curf),zch);
+ END;
+ pbufi = pbufi+t;
+ curchunk -> chunk.xe = xpos;
+ curchunk -> chunk.ie = pbufi;
+
+ END ord_text;
+
+ finish_page: PROCEDURE;
+ %REPLACE maxprch BY 175;
+ %REPLACE maxtotch BY 200;
+ DCL (i,j,xmax,xleft,nprch,ntotch) FIXED;
+ DCL (pt,pt2,pt3,pt4,obufp,lis) PTR;
+ DCL obuf CHAR(400) BASED (obufp);
+ DCL found BIT;
+
+ PUT EDIT ('[' || cvis(pageno) || '] ') (A);
+ IF top_of_page THEN DO;
+ top_of_page = false;
+ PUT EDIT(byte(12)) (A) FILE(outf);
+ END;
+
+ /* Now we have to write out all the chunks */
+ /* For now we will write them in a simple fashion */
+ DO i = 0 TO 3300;
+ IF chls(i) ^= Null() THEN DO;
+ /* MXO stuff has been omitted here */
+ lis = Null();
+ DO WHILE (chls(i) ^= Null());
+ xleft = 1000000000; /* a very high value */
+ /* Make LIS be a list of nonoverlapping chunks */
+ DO WHILE (true);
+ pt2 = Null();
+ pt3 = Null();
+ xmax = -1;
+ DO pt = chls(i) REPEAT pt -> chunk.next
+ WHILE (pt ^= Null());
+ IF pt -> chunk.xe > xmax
+ & pt -> chunk.xe <= xleft THEN DO;
+ xmax = pt -> chunk.xe;
+ pt3 = pt;
+ pt4 = pt2;
+ END;
+ pt2 = pt;
+ END;
+ IF pt3 = Null() THEN GOTO print_lis;
+ IF pt4 = Null() THEN chls(i) = chls(i) -> chunk.next;
+ ELSE pt4 -> chunk.next = pt4 -> chunk.next
+ -> chunk.next;
+ pt3 -> chunk.next = lis;
+ lis = pt3;
+ xleft = pt3 -> chunk.xs;
+ END;
+ print_lis:
+ IF lis = Null() THEN GOTO next_i;
+ DO WHILE (lis ^= Null());
+ /* Print as many chunks from LIS as will fit
+ on one line; split last chunk if necessary */
+ nprch = 0;
+ ntotch = 0;
+ PUT SKIP FILE(outf);
+ CALL escape_seq_outf(esc || '[' || cvis(i) || 'd');
+ pt = lis;
+ pt2 = Null();
+ DO WHILE (pt ^= Null());
+ pt3 = pt;
+ CALL print_pt;
+ IF pt ^= Null() THEN GOTO finished_lis;
+ pt = pt3 -> chunk.next;
+ pt2 = pt3;
+ END;
+ finished_lis:
+ IF pt2 ^= Null() THEN DO;
+ pt2 -> chunk.next = free_chunk_list;
+ free_chunk_list = lis;
+ lis = pt;
+ END;
+ END;
+ END;
+ END;
+ next_i:
+ END;
+ pbufi = 0;
+ PUT EDIT (byte(10)) (A) FILE(outf);
+ top_of_page = true;
+ pageno = pageno+1;
+
+ move_forward_outf: PROCEDURE (dist,font);
+ DCL (dist,font) FIXED;
+ DCL sdist CHAR(12) VARYING;
+
+ sdist = cvis(dist);
+ CALL escape_seq_outf(esc || '[' || sdist || 'a');
+ ntotch = ntotch+3+length(sdist);
+
+ END move_forward_outf;
+
+ escape_seq_outf: PROCEDURE (c);
+ DCL c CHAR(*);
+ DCL t FIXED;
+
+ t = length(c);
+ ntotch = ntotch+t;
+ PUT EDIT (c) (A) FILE(outf);
+
+ END escape_seq_outf;
+
+ print_pt: PROCEDURE;
+
+ DCL (chunklen,escix,uselen,base,i,j,k,font) FIXED;
+
+ /* This procedure prints as much of pt as there
+ is room left on the line for and deletes that
+ piece of pt */
+
+ base = pt -> chunk.is;
+ chunklen = pt -> chunk.ie - pt -> chunk.is;
+ uselen = min(chunklen,maxtotch-ntotch);
+ uselen = min(uselen,maxprch-nprch);
+
+ IF uselen < 1 THEN RETURN; /* shouldn't be, but... */
+
+
+/* We must ensure that PT is not split in the middle of an escape
+sequence */
+
+ IF uselen ^= chunklen THEN DO;
+ escix = -1;
+ DO i = 0 TO uselen-1;
+ IF pbuf(pt -> chunk.is+i) = esc THEN
+ escix = i;
+ END;
+ IF escix ^= -1 & uselen-escix <= 8 THEN DO;
+ DO i = escix TO uselen-1;
+ IF pbuf(pt -> chunk.is+i) = 'a' THEN
+ GOTO no_problem;
+ END;
+ uselen = escix;
+ END;
+ END;
+ no_problem:
+
+ font = txf2lnf(pt -> chunk.fs);
+ IF useno(font) = -1 THEN DO;
+ useno(whouses(maxfontnos)) = -1;
+ useno(font) = maxfontnos;
+ whouses(maxfontnos) = font;
+ CALL escape_seq_outf(esc ||
+ 'P1;1' || cvis(maxfontnos) || '}' ||
+ substr(fname(font),1,16) || esc || '\');
+ END;
+
+ CALL escape_seq_outf(esc || '[1' ||
+ cvis(useno(font)) || 'm');
+ IF pt = lis THEN CALL escape_seq_outf(esc || '['
+ || cvis(pt -> chunk.xs) || '`');
+ ELSE DO;
+ j = pt -> chunk.xs - pt2 -> chunk.xe;
+ IF j ^= 0 THEN CALL move_forward_outf(j,
+ font);
+ END;
+ obufp = addr(pbuf(pt -> chunk.is));
+ PUT EDIT (substr(obuf,1,uselen)) (A) FILE(outf);
+ nprch = nprch+uselen;
+ ntotch = ntotch+uselen;
+ IF uselen = chunklen THEN pt = Null();
+ ELSE DO;
+ k = base;
+ DO WHILE (k <= base+uselen-1);
+ IF pbuf(k) ^= esc THEN pt -> chunk.xs = pt ->
+ chunk.xs+chw(font,rank(pbuf(k)));
+ ELSE DO;
+ i = 0;
+ k = k+2;
+ DO WHILE (pbuf(k) ^= 'a');
+ i = i*10 + rank(pbuf(k))-rank('0');
+ k = k+1;
+ IF k > base+uselen-1 THEN PUT SKIP
+ EDIT ('Topp internal error.') (A);
+ END;
+ pt -> chunk.xs = pt -> chunk.xs+i;
+ END;
+ k = k+1;
+ END;
+ pt -> chunk.is = pt -> chunk.is+uselen;
+ END;
+
+ END print_pt;
+
+ END finish_page;
+
+ define_command: PROCEDURE(name,value) RETURNS(BIT);
+ DCL (name,value) CHAR(*);
+ DCL p PTR;
+ DCL (nl,vl) FIXED;
+
+ nl = length(name);
+ vl = length(value);
+ IF udcaptr+nl+vl > udcsize THEN RETURN(false);
+ DO p = udclist REPEAT p -> udcr.next WHILE (p ^= Null());
+ IF name = substr(udcarea,p -> udcname,p -> udcnamelen)
+ THEN GOTO found;
+ END;
+ ALLOCATE udcr SET (p);
+ p -> udcr.next = udclist;
+ udclist = p;
+ p -> udcname = udcaptr;
+ udcaptr = udcaptr+nl;
+ p -> udcnamelen = nl;
+ substr(udcarea,p -> udcname,p -> udcnamelen) = name;
+ p -> udcvaluelen = 0;
+ p -> udcvalue = udcaptr;
+ found:
+ IF vl = 0 THEN RETURN(true);
+ /* IF possible fit command in existing area */
+ IF vl >= p -> udcvaluelen
+ THEN DO;
+ p -> udcvalue = udcaptr;
+ udcaptr = udcaptr+vl;
+ END;
+ p -> udcvaluelen = vl;
+ substr(udcarea,p -> udcvalue,p -> udcvaluelen) = value;
+ RETURN(true);
+
+ END define_command;
+
+ read_inline: PROCEDURE;
+
+ ON ENDFILE (incfile) BEGIN;
+ CLOSE FILE (incfile);
+ reading_from_incfile = false;
+ IF incsave = '' THEN GOTO once_again;
+ ELSE DO;
+ inline = incsave;
+ GOTO return_label;
+ END;
+ END;
+
+ once_again:
+ IF reading_from_incfile THEN GET EDIT (inline) (A(511))
+ FILE(incfile);
+ ELSE GET EDIT (inline) (A(511)) FILE(inf);
+
+ return_label:
+ RETURN;
+
+ END read_inline;
+
+ move_forward: PROCEDURE (dist);
+ DCL (dist,i) FIXED;
+ DCL sdist CHAR(12) VARYING;
+
+ sdist = esc || '[' || cvis(dist) || 'a';
+
+ IF pbufi+length(sdist) >= pbufsize THEN DO;
+ PUT SKIP EDIT('LN03Topp capacity exceeded: ' ||
+ 'too much stuff on one page.') (A);
+ GOTO abort_job;
+ END;
+ DO i = 1 TO length(sdist);
+ pbuf(pbufi+i-1) = substr(sdist,i,1);
+ END;
+ pbufi = pbufi+length(sdist);
+
+ END move_forward;
+
+ new_chunk: PROCEDURE;
+
+ IF free_chunk_list = Null() THEN DO;
+ ALLOCATE chunk SET (curchunk);
+ chunkmax = chunkmax+1;
+ END; ELSE DO;
+ curchunk = free_chunk_list;
+ free_chunk_list = free_chunk_list -> chunk.next;
+ END;
+ curchunk -> chunk.fs = curf;
+ curchunk -> chunk.next = chls(ypos);
+ chls(ypos) = curchunk;
+ chunkcnt = chunkcnt+1;
+ ftime(txf2lnf(curf)) = chunkcnt;
+
+ END new_chunk;
+
+END ln03topp;
diff --git a/dviware/ln03/rose/lnfinc.pli b/dviware/ln03/rose/lnfinc.pli
new file mode 100644
index 0000000000..c8cafe8ff6
--- /dev/null
+++ b/dviware/ln03/rose/lnfinc.pli
@@ -0,0 +1,35 @@
+cvis: PROCEDURE (i) RETURNS (CHAR (*));
+ DCL i FIXED;
+
+ RETURN (frontstrip(CHAR (i)));
+END cvis;
+
+strip: PROCEDURE (t) RETURNS (CHAR (*));
+ DCL i FIXED,
+ t CHAR (*);
+
+ DO i = Length (t) TO 1 BY -1;
+ IF substr (t,i,1) ^= ' '
+ THEN RETURN (substr (t,1,i));
+ END;
+ RETURN ('');
+END strip;
+
+frontstrip: PROCEDURE (t) RETURNS (CHAR (*));
+ DCL i FIXED,
+ t CHAR (*);
+
+ DO i = 1 TO Length (t);
+ IF substr (t,i,1) ^= ' '
+ THEN RETURN (substr (t,i));
+ END;
+ RETURN ('');
+END frontstrip;
+
+upcase: PROCEDURE (text) RETURNS (CHAR (*));
+ DCL text CHAR (*);
+
+ RETURN (translate (text,
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'abcdefghijklmnopqrstuvwxyz'));
+END upcase;
diff --git a/dviware/ln03/rose/newffc.c b/dviware/ln03/rose/newffc.c
new file mode 100644
index 0000000000..3e748ebec3
--- /dev/null
+++ b/dviware/ln03/rose/newffc.c
@@ -0,0 +1,1293 @@
+/* This program is the second version of FFC, a font file converter. It
+performs certain auxiliary file management functions related to the DVI to
+LN03 translator.
+
+Copyright (c) 1984, 1985, 1986 by Digital Equipment Corporation, Maynard,
+Massachusetts. Author: Flavio Rose, ...!decwrl!dec-rhea!dec-dvinci!rose.
+
+This program is being developed. It is quite incomplete.
+
+3/7/86 Add hack in handle_rpxl to read rms files.
+ Version 5.
+
+3/14/86 Additional fixes: tnftl, snftl, tnftr, rnft. Version 6.
+
+3/19/86 ppxlr, pnftr. Version 7.
+
+4/1/86 Fixed bug in snftl -- was writing the bytes
+ in reverse into the nft buffer. Version 8.
+
+*/
+
+#ifdef vms
+#include stdio
+#include ctype
+#include fab
+#include rmsdef
+#else
+#include <stdio.h>
+#include <ctype.h>
+#endif
+
+/* In VMS we declare external variables to be globalref.
+This is not really necessary, just an old habit. */
+
+#ifdef vms
+#define GLOBAL globaldef
+#define EXTERN globalref
+#else
+#define GLOBAL
+#define EXTERN extern
+#endif
+
+/* User typein is read into an array called inline. The pointers istart and
+ilp are used to keep track of a position in inline. */
+
+GLOBAL char inline[513];
+GLOBAL int ilp,istart;
+GLOBAL char *help_string =
+"\n Available commands:\
+\n exit\
+\n rpxl tpxlr tpxll spxll wstr ppxlr\
+\n rnft tnftr tnftl snftl wnft pnftr\
+\n initnft cptonft";
+
+#define skipb for(; inline[ilp]==' '; ilp++);
+#define skipnb for(; inline[ilp]!='\0' && inline[ilp]!=' '; ilp++);
+
+/* The main program of FFC is an interactive loop that receives commands
+from the user's terminal and writes replies. */
+
+main() {
+
+ printf("\n Font File Converter 8\n");
+
+ for(;;) {
+ printf("\nFFC> ");
+ gets(inline);
+ if (feof(stdin)) goto exit_label;
+
+ ilp = 0; skipb; istart = ilp;
+ for (; inline[ilp]!='\0' && inline[ilp]!=' '; ilp++)
+#ifdef vms
+ inline[ilp] = tolower(inline[ilp]);
+#else
+ ;
+#endif
+
+ if (strncmp(&inline[istart],"rpxl",4) == 0) handle_rpxl();
+ else if (strncmp(&inline[istart],"rnft",4) == 0) handle_rnft();
+ else if (strncmp(&inline[istart],"tpxlr",5) == 0)
+ handle_tpxlr(0);
+ else if (strncmp(&inline[istart],"ppxlr",5) == 0)
+ handle_tpxlr(1);
+ else if (strncmp(&inline[istart],"tnftr",5) == 0)
+ handle_tnftr(0);
+ else if (strncmp(&inline[istart],"pnftr",5) == 0)
+ handle_tnftr(1);
+ else if (strncmp(&inline[istart],"tpxll",5) == 0)
+ handle_tpxll();
+ else if (strncmp(&inline[istart],"tnftl",5) == 0)
+ handle_tnftl();
+ else if (strncmp(&inline[istart],"spxll",5) == 0)
+ handle_spxll();
+ else if (strncmp(&inline[istart],"snftl",5) == 0)
+ handle_snftl();
+ else if (strncmp(&inline[istart],"initnft",7) == 0)
+ handle_initnft();
+ else if (strncmp(&inline[istart],"cptonft",7) == 0)
+ handle_cptonft();
+ else if (strncmp(&inline[istart],"wnft",4) == 0)
+ handle_wnft();
+ else if (strncmp(&inline[istart],"wstr",4) == 0)
+ handle_wstr();
+ else if (strncmp(&inline[istart],"exit",4) == 0)
+ goto exit_label;
+ else if (inline[istart] == '?') printf(help_string);
+ else {
+ inline[ilp] = '\0';
+ printf("\nNo such command: '%s'",&inline[istart]);
+ }
+
+ }
+
+exit_label:
+ ;
+}
+
+/* In VMS file specifications have a maximum length, which is convenient in
+sizing character arrays that are to hold them. Thus, the following
+definition. [[In Unix, there is no such convenience, so the scheme needs to
+be changed. Sigh.]] */
+
+#define FILESPECLEN 252
+
+/* PXL and LN03 format (NFT) files are simply read into memory and edited
+there. [[This is probably not reasonable on a personal computer, because of
+the 8088 segment size limit and the overall 640k memory cap.]] */
+
+#define PXLBUFSIZE 512*256
+#define NFTBUFSIZE 512*256
+
+GLOBAL unsigned char pxlbuf[PXLBUFSIZE];
+GLOBAL unsigned char nftbuf[NFTBUFSIZE];
+GLOBAL long pxllen, nftlen;
+
+/* The value conv is computed to be the correct conversion factor from the
+widths in the PXL file, which are expressed in units of 2^-20 times the
+design size, to pixels. */
+
+GLOBAL float conv;
+
+/* As usual when dealing with TeX files, we have to rearrange bytes in
+an overlay to combine them into longwords. The reason for this is that
+bytes in TeX files are combined into longwords the opposite way from how
+the VAX combines them.
+
+Hence, the following overlay and macro: [[It ought to work on 8088s too,
+but not on 68000s.]] */
+
+GLOBAL union lc { unsigned long int ul;
+ long int l;
+ unsigned char c[4]; } lcx;
+
+#define copy_from_pxl(_x) { lcx.c[3] = pxlbuf[_x]; \
+ lcx.c[2] = pxlbuf[(_x)+1]; lcx.c[1] = pxlbuf[(_x)+2]; \
+ lcx.c[0] = pxlbuf[(_x)+3]; }
+
+/* Handle_rpxl handles an rpxl command, which orders FFC to read a PXL file
+into the pxlbuf.
+
+To speed up the operation of this program, the reading is done with Unix
+I/O. In VMS only, we open the file twice, once to find out its record
+attributes, and again to actually read it. The record attributes are
+relevant because in some cases we have to explicitly discard carriage
+returns depending on them.
+
+[[The VAX C RTL fstat function should allow us to avoid this double open,
+but doesn't seem to work properly.]] */
+
+int handle_rpxl() {
+
+ long i,j;
+ int pxlf;
+ float two_to_the_20th;
+#ifdef vms
+ char discard;
+#endif
+
+ skipb; istart = ilp; skipnb;
+ inline[ilp] = '\0';
+#ifdef vms
+ discard = VMS_file_check(&inline[istart],".pxl");
+#endif
+ pxlf = open_input_file(&inline[istart],".pxl");
+ if (pxlf == -1) {
+ printf("\nUnable to open file %s",&inline[istart]);
+ return(1);
+ }
+ pxllen = 0;
+ while (1) {
+ j = PXLBUFSIZE-pxllen;
+ if (j > 65535) j = 65535;
+ i = read(pxlf,&pxlbuf[pxllen],j);
+ if (i < 0) {
+ printf("\nError while reading PXL file.");
+ close(pxlf);
+ return(1);
+ }
+ if (i == 0) break;
+ pxllen += i;
+#ifdef vms
+ if (discard != 0) pxllen--;
+#endif
+ }
+ if (i != 0) {
+ printf("\nPXL file too large for FFC's buffer (%d bytes long)",
+ PXLBUFSIZE-1);
+ close(pxlf);
+ return(1);
+ }
+ close(pxlf);
+
+/* Now that we have read the PXL file, check that it is in correct format
+by looking for a trailing ID byte of 1001. */
+
+ if (locate_pxldir() != 0) return(1);
+
+/* If the format is correct, derive the design size and magnification from
+the final longwords of the PXL file and print them. */
+
+ copy_from_pxl(pxllen-12);
+ conv = lcx.ul;
+ two_to_the_20th = 0x100000;
+ printf("\nDesign size %.2f points",conv/two_to_the_20th);
+ copy_from_pxl(pxllen-16);
+ conv = (conv/two_to_the_20th)*(lcx.ul/5.0)*(1.0/(72.27*two_to_the_20th));
+ printf("\nPixels per em = %.1f",conv*two_to_the_20th);
+ printf("\nMagnification = %.3f (at 300 dpi)",lcx.ul/1500.0);
+
+}
+
+#define copy_to_nft(_x) { nftbuf[_x] = lcx.c[0]; \
+ nftbuf[(_x)+1] = lcx.c[1]; nftbuf[(_x)+2] = lcx.c[2]; \
+ nftbuf[(_x)+3] = lcx.c[3]; }
+#define copy_from_nft(_x) { lcx.c[0] = nftbuf[_x]; \
+ lcx.c[1] = nftbuf[(_x)+1]; lcx.c[2] = nftbuf[(_x)+2]; \
+ lcx.c[3] = nftbuf[(_x)+3]; }
+
+GLOBAL int nft_first,nft_last,nft_next_code;
+
+int handle_rnft() {
+
+ long i,j;
+ int nftf;
+ float two_to_the_20th;
+#ifdef vms
+ char discard;
+#endif
+
+ skipb; istart = ilp; skipnb;
+ inline[ilp] = '\0';
+#ifdef vms
+ discard = VMS_file_check(&inline[istart],".nft");
+#endif
+ nftf = open_input_file(&inline[istart],".nft");
+ if (nftf == -1) {
+ printf("\nUnable to open file %s",&inline[istart]);
+ return(1);
+ }
+ nftlen = 0;
+ while (1) {
+ j = NFTBUFSIZE-nftlen;
+ if (j > 65535) j = 65535;
+ i = read(nftf,&nftbuf[nftlen],j);
+ if (i < 0) {
+ printf("\nError while reading NFT file.");
+ close(nftf);
+ return(1);
+ }
+ if (i == 0) break;
+ nftlen += i;
+#ifdef vms
+ if (discard != 0) nftlen--;
+#endif
+ }
+ if (i != 0) {
+ printf("\nNFT file too large for FFC's buffer (%d bytes long)",
+ NFTBUFSIZE-1);
+ close(nftf);
+ return(1);
+ }
+ close(nftf);
+ printf("\n %d bytes read",nftlen);
+
+#define fnt__l_first_character 164
+
+ copy_from_nft(fnt__l_first_character);
+ nft_first = lcx.ul;
+ copy_from_nft(fnt__l_first_character+4);
+ nft_last = lcx.ul;
+ printf("\n First character %d, last character %d",nft_first,nft_last);
+ return(0);
+
+}
+
+int open_input_file (s,ext)
+char s[], ext[];
+{
+ char fs[FILESPECLEN];
+ int jnam,jext;
+
+ strcpy(fs,s);
+ find_filename(fs,&jnam,&jext);
+ if (fs[jext] == '\0') strcat(fs,ext);
+ return(open(fs,0,0));
+
+}
+
+#ifdef vms
+int VMS_file_check (s,ext)
+char s[], ext[];
+{
+ char fs[FILESPECLEN];
+ int jnam,jext,rv,i;
+ struct FAB the_fab;
+
+ strcpy(fs,s);
+ find_filename(fs,&jnam,&jext);
+ if (fs[jext] == '\0') strcat(fs,ext);
+ the_fab = cc$rms_fab;
+ (the_fab).fab$l_fna = fs;
+ (the_fab).fab$b_fns = strlen(fs);
+ (the_fab).fab$b_fac = FAB$M_BIO;
+ i = sys$open(&(the_fab));
+ if (i % 2 == 0) return(0);
+ rv = 0;
+ if (the_fab.fab$b_rat == FAB$M_CR && the_fab.fab$b_rfm == FAB$C_VAR)
+ rv = 1;
+ sys$close(the_fab);
+ return(rv);
+
+}
+#endif
+
+/* Find_filename finds the filename part of a VMS filespec passed in s,
+returning the index of the first character in *ns, and the index of the
+character after the last in *ne. [[This has now been fixed to handle Ultrix
+as well.]] */
+
+int find_filename(s,ns,ne)
+char s[];
+int *ns,*ne;
+{
+ int jnam,jext,j,slen;
+
+ slen = strlen(s);
+ jnam = 0;
+ for (j = slen-1; j >= 0; j--) {
+#ifdef vms
+ if (s[j] == ':' || s[j] == ']' ||
+ s[j] == '>') {
+#else
+ if (s[j] == '/') {
+#endif
+ jnam = j+1;
+ break;
+ }
+ }
+
+ jext = slen;
+ for (j = jnam; j < slen; j++) {
+#ifdef vms
+ if (s[j] == '.' || s[j] == ';') {
+#else
+ if (s[j] == '.') {
+#endif
+ jext = j;
+ break;
+ }
+ }
+
+ *ns = jnam;
+ *ne = jext;
+}
+
+/* PXL files have a "directory" at the end. Locate_pxldir searches for the
+PXL id 1001, starting at the end of the PXL file. The directory is 517
+longwords back of there. If locate_pxldir can't find the PXL id, it outputs
+an error message. */
+
+GLOBAL long pxldir;
+
+int locate_pxldir () {
+
+ long i;
+
+ pxldir = 0;
+ if (pxllen < 517*4) {
+ printf("\nPXL file too short, must be at least 517 longwords.");
+ return(1);
+ }
+ if (pxllen%4 != 0) {
+ printf("\nPXL file length should be multiple of 4.");
+ pxllen -= pxllen%4;
+ }
+
+ for (i=pxllen-4; i>0; i-=4) {
+ copy_from_pxl(i);
+ if (lcx.ul == 1001) {
+ pxllen = i+4;
+ if (pxllen < 517*4) {
+ printf("\nPXL file too short, must be at least 517 longwords");
+ return(1);
+ }
+ pxldir = pxllen-517*4;
+ printf("\n%d bytes read.",pxllen);
+ return(0);
+ }
+ }
+ printf("\nUnable to find directory in PXL file.");
+ return(1);
+}
+
+/* The tpxll command instructs FFC to type a longword from the PXL file. */
+
+int handle_tpxll() {
+
+ long i;
+
+ i = atoi(&inline[ilp]);
+ if (i < 0 || i > pxllen-4) {
+ printf("\nRequested location %ld lies outside PXL file.",i);
+ return(1);
+ } else {
+ copy_from_pxl(i);
+ printf("\n%ld: %10lu = %%x\"%08lx\"",i,lcx.ul,lcx.ul);
+ return(0);
+ }
+}
+
+/* The tnftl command instructs FFC to type a longword from the nft file. */
+
+int handle_tnftl() {
+
+ long i;
+
+ i = atoi(&inline[ilp]);
+ if (i < 0 || i > nftlen-4) {
+ printf("\nRequested location %ld lies outside NFT file.",i);
+ return(1);
+ } else {
+ copy_from_nft(i);
+ printf("\n%ld: %10lu = %%x\"%08lx\"",i,lcx.ul,lcx.ul);
+ return(0);
+ }
+}
+
+/* The spxll command sets a longword in the pxl file to a value, expressed
+in HEXADECIMAL. */
+
+int handle_spxll()
+{
+ long i;
+
+ skipb;
+ i = atoi(&inline[ilp]);
+ if (i < 0 || i > pxllen-4) {
+ printf("\nRequested location %d lies outside PXL file.",i);
+ return(1);
+ }
+ skipnb;
+ copy_from_pxl(i);
+ sscanf(&inline[ilp],"%X",&lcx.ul);
+ pxlbuf[i] = lcx.c[3]; pxlbuf[i+1] = lcx.c[2];
+ pxlbuf[i+2] = lcx.c[1]; pxlbuf[i+3] = lcx.c[0];
+ printf("\n%ld: %10lu = %%x\"%08lx\"",i,lcx.ul,lcx.ul);
+ return(0);
+}
+
+/* The snftl command sets a longword in the nft file to a value, expressed
+in HEXADECIMAL. */
+
+int handle_snftl()
+{
+ long i;
+
+ skipb;
+ i = atoi(&inline[ilp]);
+ if (i < 0 || i > nftlen-4) {
+ printf("\nRequested location %d lies outside NFT file.",i);
+ return(1);
+ }
+ skipnb;
+ copy_from_nft(i);
+ sscanf(&inline[ilp],"%X",&lcx.ul);
+ nftbuf[i+3] = lcx.c[3]; nftbuf[i+2] = lcx.c[2];
+ nftbuf[i+1] = lcx.c[1]; nftbuf[i] = lcx.c[0];
+ printf("\n%ld: %10lu = %%x\"%08lx\"",i,lcx.ul,lcx.ul);
+ return(0);
+}
+
+#define pxl_word(_x) (pxlbuf[_x]*256+pxlbuf[(_x)+1])
+#define signed_pxl_word(_x) ((pxl_word(_x)>0x8000) ? \
+ (pxl_word(_x)-0x10000) : pxl_word(_x))
+
+GLOBAL char visible_byte[9],sixel_line[401];
+
+/* Handle_tpxlr handles the tpxlr and ppxlr commands, which direct FFC to
+type or plot the rasters corresponding to a character code in the PXL file.
+The parameter how determines what to do: type if how = 0, plot if how = 1.
+
+Plotting is done using the VT125/VT240 sixel protocol. VT100 ANSI escape
+sequences are used to clear the screen and set the current screen position,
+in order to keep the sixels from overwriting text. If the user's terminal
+can't handle these protocols, a hopefully small amount of gobbledygook will
+be displayed. [[On the VT125, text will eventually begin to scroll over the
+last sixels drawn; the user must clear the screen manually.]]
+
+We display not just the rasters themselves, but also the directory
+information for the character. This information comprises the byte offset
+of the rasters within the PXL file; the number of rows and columns in the
+rasters; the location of the reference point of the glyph with respect to
+the upper left corner of the rasters; and the width of the glyph. */
+
+int handle_tpxlr (how)
+int how;
+{
+
+ int code;
+ unsigned int rows,cols,i,j,k,l,m,col_longs;
+ long ds,rs;
+ unsigned long wid;
+
+ if (pxldir == 0) {
+ printf("\nCan't display rasters because there isn't a good PXL");
+ printf(" file in the PXL buffer.");
+ return(1);
+ }
+
+ code = atoi(&inline[ilp]);
+ if (code < 0 || code > 127) {
+ printf("\nCharacter code must be between 0 and 127");
+ return(1);
+ }
+
+/* Determine the location of the rasters within the PXL file. */
+
+ ds = pxldir+16*code;
+ copy_from_pxl(ds+8);
+ rs = 4*lcx.ul;
+ if (rs == 0) {
+ printf("\nNo rasters for code %d",code);
+ return(0);
+ }
+ if (how == 0 || rs > pxllen) printf("\n Rasters begin at %d",rs);
+ if (rs > pxllen) {
+ printf("\n ... outside the PXL file");
+ return(1);
+ }
+
+/* Compute the remaining directory information for the glyph. */
+
+ cols = pxl_word(ds); rows = pxl_word(ds+2);
+ copy_from_pxl(ds+12);
+ wid = lcx.ul;
+ col_longs = (cols+31)/32;
+
+/* Plot as sixels if that is required. */
+
+ if (how == 1) {
+ printf("\033[2J"); /* erase entire screen */
+ printf("\033Pq"); /* go into sixel mode */
+ for (i=0; i<rows; i+=6) {
+ for (j=0; j<cols; j++) sixel_line[j] = 0;
+ for (j=i; j<rows && j<i+6; j++) {
+ m = (1<<(j-i));
+ for (k=0; 8*k<cols; k++) {
+ rev_binrep(pxlbuf[rs+4*col_longs*j+k]);
+ for (l=0; l<8; l++)
+ if (visible_byte[l] == '8')
+ sixel_line[8*k+l] |= m;
+ }
+ }
+ output_sixel_line(cols);
+ }
+ printf("\033\\"); /* leave sixel mode */
+ printf("\033[%d;1H",(rows/10)+1); /* position cursor after sixels */
+ printf("\n Rasters begin at %d",rs);
+ }
+
+ printf("\n %d rows, %d columns",rows,cols);
+ printf("\n width %.2f pixels",conv*wid);
+ printf("\n xoffset %d, yoffset %d",signed_pxl_word(ds+4),
+ signed_pxl_word(ds+6));
+
+/* Now print out the rasters themselves. Compute k as the number of
+longwords required to hold each row of rasters. */
+
+ if (how == 0) {
+ for (l=0; l<rows; l++) {
+ putc('\n',stdout);
+ for (m=0; 8*m<cols; m++) {
+ rev_binrep(pxlbuf[rs+4*col_longs*l+m]);
+ printf(visible_byte);
+ }
+ }
+ }
+}
+
+/* Optimization of this to run-length encode the output isn't really
+necessary at this stage. But it's fun to do anyway. */
+
+output_sixel_line(length)
+int length;
+{
+ int i,howmany,sbp;
+ char last,sixel_buf[802];
+
+ last = 0;
+ howmany = 0;
+ sbp = 0;
+ sixel_line[length] = 0;
+ for (i=0; i<=length; i++) {
+ if (sixel_line[i] == last) howmany++;
+ else {
+ if (howmany == 1) {
+ sixel_buf[sbp] = 63+last;
+ sixel_buf[sbp+1] = 63+last;
+ sbp += 2;
+ } else if (howmany > 1) {
+ sixel_buf[sbp] = '!';
+ if (howmany < 5) {
+ sixel_buf[sbp+1] = 2*howmany+'0';
+ sbp += 2;
+ } else if (howmany < 50) {
+ sixel_buf[sbp+1] = (2*howmany)/10+'0';
+ sixel_buf[sbp+2] = (2*howmany)%10+'0';
+ sbp += 3;
+ } else {
+ sixel_buf[sbp+1] = (2*howmany)/100+'0';
+ sixel_buf[sbp+2] = ((2*howmany)%100)/10+'0';
+ sixel_buf[sbp+3] = (2*howmany)%10+'0';
+ sbp += 4;
+ }
+ sixel_buf[sbp++] = last+63;
+ }
+ last = sixel_line[i];
+ howmany = 1;
+ }
+ }
+ sixel_buf[sbp] = 0;
+ printf("\n%s-",sixel_buf);
+
+}
+
+
+/* Place the reversed binary representation of v in the visible_byte
+array, using dots . for 0s and 8s for 1s. */
+
+int rev_binrep (v)
+int v;
+{
+ int cnt,rem,quo;
+
+ strcpy(visible_byte,"........");
+ quo = v;
+ for (cnt=0; cnt<8; cnt++) {
+ rem = quo%2; quo = quo/2;
+ if (rem != 0) visible_byte[7-cnt] = '8';
+ }
+}
+
+int handle_tnftr (how)
+int how;
+{
+
+ int code;
+ int rows,columns,quo;
+ long i,j,k,l,m,def_start,xoff,yoff,col_bytes;
+
+ code = atoi(&inline[ilp]);
+
+ if (code < nft_first || code > nft_last) {
+ printf("\n No such character.");
+ return(1);
+ }
+
+/* First we need to determine def_start, the byte offset of the character
+definition for code from in the NFT file */
+
+ copy_from_nft(480+(code-nft_first)*4);
+ def_start = lcx.l;
+
+ if (def_start == 0) {
+ printf("\n No such character -- zero locator.");
+ return(1);
+ }
+
+/* Now we need to check that the rasters are uncompressed. LN03s can't
+accept compressed rasters, and this program isn't clever enough to
+understand them either. */
+
+ if (nftbuf[def_start+17] != 129) {
+ printf("\n Can't handle compressed rasters.");
+ return(1);
+ }
+
+ copy_from_nft(def_start+4);
+
+ if (nftbuf[def_start+16] % 2 == 0) {
+ rows = nftbuf[def_start+20]+256*nftbuf[def_start+21];
+ columns = nftbuf[def_start+22]+256*nftbuf[def_start+23];
+ } else {
+ columns = nftbuf[def_start+20]+256*nftbuf[def_start+21];
+ rows = nftbuf[def_start+22]+256*nftbuf[def_start+23];
+ }
+ col_bytes = columns/8;
+ if (columns != 8*col_bytes) col_bytes++;
+
+ if (how == 1) {
+ printf("\033[2J"); /* erase entire screen */
+ printf("\033Pq"); /* go into sixel mode */
+ for (i=0; i<rows; i+=6) {
+ for (j=0; j<columns; j++) sixel_line[j] = 0;
+ for (j=i; j<rows && j<i+6; j++) {
+ m = (1<<(j-i));
+ for (k=0; 8*k<columns; k++) {
+ binrep(nftbuf[def_start+24+col_bytes*j+k]);
+ for (l=0; l<8; l++)
+ if (visible_byte[l] == '8')
+ sixel_line[8*k+l] |= m;
+ }
+ }
+ output_sixel_line(columns);
+ }
+ printf("\033\\"); /* leave sixel mode */
+ printf("\033[%d;1H",(rows/10)+1); /* position cursor after sixels */
+ }
+
+ printf("\n Rasters begin at %d",def_start+24);
+ if (nftbuf[def_start+16] % 2 == 0) printf("\n stored portrait");
+ else printf("\n stored landscape");
+ printf("\n %d rows, %d columns",rows,columns);
+ printf("\n width %.2f pixels",((double)lcx.l)/24.0);
+ copy_from_nft(def_start+8);
+ xoff = lcx.l;
+ copy_from_nft(def_start+12);
+ yoff = lcx.l;
+ printf("\n xoffset %.2f, yoffset %.2f",-((double)xoff)/24.0,
+ -((double)yoff)/24.0);
+
+/* Now do the actual typing. */
+
+/* writeln(i:1,' rows ',j:1,' columns'); */
+
+ if (how == 1) return(0);
+ for (l = 0; l < rows; l++) {
+ printf("\n");
+ for (m = 0; m < col_bytes; m++) {
+ binrep(nftbuf[def_start+24+col_bytes*l+m]);
+ printf("%s",visible_byte);
+ }
+ }
+
+ return(0);
+
+}
+
+/* Place the reversed binary representation of v in the visible_byte
+array, using dots . for 0s and 8s for 1s. */
+
+int binrep (v)
+int v;
+{
+ int cnt,rem,quo;
+
+ strcpy(visible_byte,"........");
+ quo = v;
+ for (cnt=0; cnt<8; cnt++) {
+ rem = quo%2; quo = quo/2;
+ if (rem != 0) visible_byte[cnt] = '8';
+ }
+}
+
+GLOBAL char buffer_fixedp;
+
+/* The initnft command reads two character codes from the command line. If
+both are acceptable, it initializes the nft buffer to hold reasonable
+values for an nft file containing glyphs for codes between the two read
+from the command line. */
+
+int handle_initnft() {
+
+ int c1,c2;
+
+ skipb;
+ c1 = atoi(&inline[ilp]);
+ skipnb; skipb;
+ c2 = atoi(&inline[ilp]);
+
+ if (c1 < 0 || c1 > 255 || c2 < 0 || c2 > 255 || c1 > c2) {
+ printf("\n Bad character code range: [%d, %d]",c1,c2);
+ return(1);
+ }
+ nft_first = c1; nft_last = c2;
+ init_nft();
+ buffer_fixedp = 0;
+ return(0);
+}
+
+int init_nft()
+{
+ int i;
+
+/* The following array holds reasonable values for bytes 0-479 of an NFT
+file. These bytes were copied from a font load which the LN03 was known to
+accept. The NFT files generated by this program are initialized using these
+values. Some of these get overwritten later, however. */
+
+ static char good_opening[512] = {
+ 104,38,0,0,70,79,78,84,
+ 1,0,0,0,31,0,0,0,
+ 20,0,0,0,85,48,48,48,
+ 48,48,48,48,48,50,83,75,
+ 48,48,71,71,48,48,48,49,
+ 85,90,90,90,90,48,50,70,
+ 48,48,48,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 181,7,9,0,11,0,14,0,
+ 0,0,0,0,104,0,0,0,
+ 252,0,0,0,124,0,0,0,
+ 100,1,0,0,120,1,0,0,
+ 224,1,0,0,4,0,0,0,
+ 88,3,0,0,0,0,0,0,
+ 92,3,0,0,48,0,0,0,
+ 92,3,0,0,0,0,0,0,
+ 140,3,0,0,212,34,0,0,
+ 140,3,0,0,33,0,0,0,
+ 126,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,0,0,168,0,0,0,
+ 16,0,0,0,94,0,0,0,
+ 0,0,0,0,94,0,0,0,
+ 0,0,0,0,94,0,0,0,
+ 0,0,0,0,236,25,0,0,
+ 54,25,0,0,14,27,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2,0,0,0,
+ 7,0,0,0,92,3,0,0,
+ 7,0,0,0,99,3,0,0,
+ 16,0,0,0,106,3,0,0,
+ 16,0,0,0,122,3,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 10,0,0,0,244,1,0,0,
+ 0,0,24,0,16,0,0,0,
+ 16,0,0,0,1,0,1,0,
+ 1,0,1,0,0,0,1,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 30,0,0,0,20,0,0,0,
+ 196,255,255,255,20,0,0,0,
+ 106,255,255,255,20,0,0,0,
+ 0,0,1,0,0,0,30,0,
+ 166,255,255,255,0,0,0,0,
+ 40,0,0,0,0,0,0,0,
+ 60,0,0,0,240,0,0,0,
+ 60,0,0,0,100,0,0,0,
+ 240,0,0,0,120,0,0,0,
+ 40,0,0,0,120,0,0,0,
+ 96,255,255,255,136,255,255,255,
+ 186,255,255,255,240,0,0,0,
+ 76,255,255,255,60,0,0,0,
+ 160,0,0,0,120,0,0,0,
+ 20,0,0,0,20,0,0,0,
+ 140,3,0,0,194,3,0,0,
+ 244,3,0,0,160,4,0,0,
+ 254,4,0,0,162,5,0,0,
+ 46,6,0,0,84,6,0,0};
+ int pool_beg,nft_chardir;
+
+/* The character directory always begins at position 480 in the Common
+Font File Format. */
+
+ nft_chardir = 480;
+
+/* Copy the good values into nftbuf */
+
+ for (i=0; i<480; i++)
+ nftbuf[i] = good_opening[i];
+ for (i=480; i<480+(nft_last-nft_first+1)*4; i++)
+ nftbuf[i] = 0;
+
+/* Set the first and last character codes */
+
+ nftbuf[fnt__l_first_character] = nft_first;
+ nftbuf[fnt__l_first_character+4] = nft_last;
+
+#define fnt__l_char_directory 116
+
+ lcx.ul = 4*(nft_last-nft_first+1);
+ copy_to_nft(fnt__l_char_directory);
+
+/* Set the character counts */
+
+#define fnt__l_infile_locators 204
+#define fnt__l_character_definitions 212
+#define fnt__l_raster_count 220
+
+ i = nft_last-nft_first+1;
+ nftlen = nft_chardir+4*i+4+48;
+ nftbuf[fnt__l_infile_locators] = i;
+ nftbuf[fnt__l_character_definitions] = i;
+ nftbuf[fnt__l_raster_count] = i;
+
+/* We now have to set some fields in the font format that are pointers to
+where other fields begin. These pointers depend on the number of glyphs we
+are placing in the font file. */
+
+#define fnt__a_subset_tables 128
+
+ lcx.ul = nft_chardir+4*i;
+ copy_to_nft(fnt__a_subset_tables);
+
+ lcx.ul = lcx.ul+4;
+ copy_to_nft(fnt__a_subset_tables+8);
+ copy_to_nft(fnt__a_subset_tables+16);
+
+ lcx.ul = lcx.ul+48;
+ copy_to_nft(fnt__a_subset_tables+24);
+
+#define fnt__a_char_definitions 160
+
+ copy_to_nft(fnt__a_char_definitions);
+
+/* Set up the string pool (48 bytes). */
+
+ pool_beg = nftlen-48;
+ strncpy(&nftbuf[pool_beg],"0B\tZZZZ",7);
+
+/* For reasons that are beyond my comprehension, the Format requires us to
+copy the first seven characters of the font file id into one piece of the
+string pool, and the first sixteen into another place. */
+
+#define fnt__t_font_file_id 20
+
+ for (i=1; i<8; i++)
+ nftbuf[pool_beg+7+i-1] = nftbuf[fnt__t_font_file_id+i-1];
+ for (i=1; i<17; i++)
+ nftbuf[pool_beg+7+7+i-1] = ' ';
+ for (i=1; i<17; i++)
+ nftbuf[pool_beg+7+7+16+i-1] = nftbuf[fnt__t_font_file_id+i-1];
+
+/* Now make string descriptors point into the string pool. */
+
+#define fnt__a_char_set 260
+#define fnt__a_family_id 268
+#define fnt__a_family_name 276
+#define fnt__a_font_id 284
+
+ lcx.ul = pool_beg;
+ copy_to_nft(fnt__a_char_set);
+ lcx.ul = pool_beg+7;
+ copy_to_nft(fnt__a_family_id);
+ lcx.ul = pool_beg+7+7;
+ copy_to_nft(fnt__a_family_name);
+ lcx.ul = pool_beg+7+7+16;
+ copy_to_nft(fnt__a_font_id);
+
+ nft_next_code = nft_first;
+
+#define fnt__l_portrait_byte_count 228
+#define fnt__l_landscape_byte_count 232
+#define fnt__l_mixed_byte_count 236
+
+ lcx.ul = 0;
+ copy_to_nft(fnt__l_portrait_byte_count);
+ copy_to_nft(fnt__l_landscape_byte_count);
+ copy_to_nft(fnt__l_mixed_byte_count);
+
+}
+
+/* Cptonft copies one or more glyphs from the PXL file to the NFT file
+buffer. It reads from the command line two codes. Beginning at the first
+code and proceeding sequentially, it copies glyphs. As cptonft copies, it
+updates nftlen and nft_next_code to reflect what is going on, as well as
+the portrait, landscape and mixed sizes. */
+
+int handle_cptonft () {
+
+ int i,c1,c2;
+
+ skipb;
+ c1 = atoi(&inline[ilp]);
+ skipnb; skipb;
+ c2 = atoi(&inline[ilp]);
+
+ if (c1 < 0 || c1 > 127 || c2 < 0 || c2 > 127 || c1 > c2) {
+ printf("\nBad range of codes: [%d, %d]",c1,c2);
+ return(1);
+ }
+ if (nft_next_code > nft_last) {
+ printf("\nCan't copy more glyphs -- no slots left in NFT file.");
+ return(1);
+ }
+ if (pxldir == 0) {
+ printf("\nCan't copy because there isn't a good PXL file in memory.");
+ return(1);
+ }
+ if (c2-c1+1 > nft_last+1-nft_next_code) {
+ c2 = c1+nft_last-nft_next_code;
+ printf("\nCopying only up to code %d",c2);
+ printf("\n -- no slots left for more glyphs in the NFT file.");
+ }
+
+ if (buffer_fixedp) {
+ nftlen -= 8;
+ buffer_fixedp = 0;
+ }
+
+ for(i=c1; i<=c2; i++)
+ if (copy_glyph(i) != 0) return(1);
+ return(0);
+}
+
+GLOBAL unsigned char rev_byte[256] = {
+ 0,128,64,192,32,160,96,224,
+ 16,144,80,208,48,176,112,240,
+ 8,136,72,200,40,168,104,232,
+ 24,152,88,216,56,184,120,248,
+ 4,132,68,196,36,164,100,228,
+ 20,148,84,212,52,180,116,244,
+ 12,140,76,204,44,172,108,236,
+ 28,156,92,220,60,188,124,252,
+ 2,130,66,194,34,162,98,226,
+ 18,146,82,210,50,178,114,242,
+ 10,138,74,202,42,170,106,234,
+ 26,154,90,218,58,186,122,250,
+ 6,134,70,198,38,166,102,230,
+ 22,150,86,214,54,182,118,246,
+ 14,142,78,206,46,174,110,238,
+ 30,158,94,222,62,190,126,254,
+ 1,129,65,193,33,161,97,225,
+ 17,145,81,209,49,177,113,241,
+ 9,137,73,201,41,169,105,233,
+ 25,153,89,217,57,185,121,249,
+ 5,133,69,197,37,165,101,229,
+ 21,149,85,213,53,181,117,245,
+ 13,141,77,205,45,173,109,237,
+ 29,157,93,221,61,189,125,253,
+ 3,131,67,195,35,163,99,227,
+ 19,147,83,211,51,179,115,243,
+ 11,139,75,203,43,171,107,235,
+ 27,155,91,219,59,187,123,251,
+ 7,135,71,199,39,167,103,231,
+ 23,151,87,215,55,183,119,247,
+ 15,143,79,207,47,175,111,239,
+ 31,159,95,223,63,191,127,255
+ };
+
+/* Copy_glyph copies the rasters and character directory information
+corresponding to code i in the PXL file, into the nft_next_code position of
+the NFT file buffer. */
+
+int copy_glyph(code)
+int code;
+{
+
+ unsigned long ds,rs,i,j;
+ unsigned int rows,cols,k,l,m,n;
+ int xoffset, yoffset;
+ float width;
+ char all_blank;
+
+/* Locate the definition and the rasters for code in the PXL file. Check
+that the rasters are indeed within the pxl file. [[This check may need to
+be modified if we implement commands to add rasters to the pxl file.]] */
+
+ ds = pxldir+16*code;
+ copy_from_pxl(ds+8);
+ rs = 4*lcx.ul;
+ cols = pxl_word(ds); rows = pxl_word(ds+2);
+ if (rs+rows*4*((cols+31)/32) > pxllen) {
+ printf("\nPXL file entry for code %d points outside file",code);
+ printf("\nCopy to NFT file terminated before code %d",nft_next_code);
+ return(1);
+ }
+
+/* If a glyph has no rasters, an "undocumented feature" of the LN03 seems
+to cause the glyph to be printed incorrectly. Because of this, we set the
+number of rows and columns to 1, and put in a blank byte (below). */
+
+ all_blank = (rows == 0) & (cols == 0);
+ if (all_blank) {
+ cols = 1;
+ rows = 1;
+ }
+
+/* Check that we have enough room left in the NFT buffer. */
+
+ if (nftlen+rows*((cols+7)/32)+24 > NFTBUFSIZE) {
+ printf("\nNo more room in NFT file buffer (%ld bytes long)",
+ NFTBUFSIZE);
+ printf("\nCopy to NFT file terminated before code %d",nft_next_code);
+ return(1);
+ }
+
+/* Compute the width of the glyph in pixels, the xoffset, and the yoffset.
+*/
+
+ copy_from_pxl(ds+12);
+ width = conv*lcx.ul;
+ xoffset = signed_pxl_word(ds+4);
+ yoffset = signed_pxl_word(ds+6);
+
+/* Clear the character definition area in the NFT file buffer. */
+
+ for(i=0; i<24+rows*((cols+7)/8); i++)
+ nftbuf[nftlen+i] = 0;
+
+/* Set the fields in the first six longwords of the NFT character
+definition. The first assignment sets the so-called "flag flag", which must
+be always be set according to the DEC Common Font File Format. A conversion
+factor of 24 is used in converting pixel values, because the values are
+supposed to be in centipoints in the NFT file, and if we assume there are
+300 pixels in an inch, then there are 7200 centipoints in an inch.
+[[Eventually, the resolution of the printer will have to be settable!]] */
+
+ nftbuf[nftlen+3] = 0x80;
+ lcx.ul = 24*width+0.5;
+ copy_to_nft(nftlen+4);
+ lcx.l = -24*xoffset;
+ copy_to_nft(nftlen+8);
+ lcx.l = -24*yoffset;
+ copy_to_nft(nftlen+12);
+
+/* The rasters are always placed in portrait into the NFT file, with no
+use of run-length encoding. Thus, the orient field in the raster format
+will be 0, and the Type 1 field is set to 0x81. */
+
+ nftbuf[nftlen+17] = 0x81;
+ nftbuf[nftlen+20] = rows%256;
+ nftbuf[nftlen+21] = rows/256;
+ nftbuf[nftlen+22] = cols%256;
+ nftbuf[nftlen+23] = cols/256;
+
+/* Now we copy the rasters themselves. As we do so, we have to reverse
+the bits within each byte. */
+
+ k = (cols+31)/32;
+ n = (cols+7)/8;
+ if (!all_blank)
+ for (l=0; l<rows; l++)
+ for (m=0; m<n; m++)
+ nftbuf[nftlen+24+n*l+m] = rev_byte[pxlbuf[rs+4*k*l+m]];
+
+/* Set the character locator, increment the nft length, and recompute
+the three sizes. */
+
+ lcx.ul = nftlen;
+ copy_to_nft(480+4*(nft_next_code-nft_first));
+ nftlen += 24+rows*n;
+ if (nftlen%2 == 1) nftlen++;
+
+ i = rows*((cols+7)/8);
+ copy_from_nft(fnt__l_portrait_byte_count);
+ lcx.ul += i;
+ copy_to_nft(fnt__l_portrait_byte_count);
+ j = ((rows+7)/8)*cols;
+ copy_from_nft(fnt__l_landscape_byte_count);
+ lcx.ul += j;
+ copy_to_nft(fnt__l_landscape_byte_count);
+ copy_from_nft(fnt__l_mixed_byte_count);
+ lcx.ul += (i > j) ? i : j;
+ copy_to_nft(fnt__l_mixed_byte_count);
+
+ nft_next_code++;
+
+ return(0);
+}
+
+/* The wnft command writes the contents of an NFT buffer into a file.
+It is necessary to do some fixup on the buffer the first time this
+is done. */
+
+int handle_wnft() {
+
+ int nftf;
+ unsigned long i;
+
+ skipb; istart = ilp; skipnb;
+ inline[ilp] = '\0';
+
+ nftf = open_output_file(&inline[istart],".nft");
+ if (nftf == -1) {
+ printf("\nUnable to open NFT file %s",&inline[istart]);
+ return(1);
+ }
+
+/* At this point we have to clean up a few fields before writing
+the file. */
+
+#define fnt__l_char_definitions_length 156
+
+ if (!buffer_fixedp) {
+ lcx.ul = nftlen-(480+(nft_last-nft_first+1)*4+48+4);
+ copy_to_nft(fnt__l_char_definitions_length);
+
+ nftlen += 8;
+ lcx.ul = nftlen;
+ copy_to_nft(0);
+ copy_to_nft(nftlen-8);
+ nftbuf[nftlen-4] = 'F';
+ nftbuf[nftlen-3] = 'O';
+ nftbuf[nftlen-2] = 'N';
+ nftbuf[nftlen-1] = 'T';
+ buffer_fixedp = 0;
+ }
+
+/* Pad the file out to a multiple of 512 bytes */
+
+ if (nftlen%512 != 0)
+ for (i=0; i<512-nftlen%512; i++)
+ nftbuf[nftlen+i] = 0;
+
+/* Now for the actual writing */
+
+ for (i=0; i<nftlen; i+=512)
+ if (write(nftf,&nftbuf[i],512) == -1) {
+ printf("\nError writing NFT file.");
+ close(nftf);
+ return(1);
+ }
+
+ close(nftf);
+ return(1);
+
+}
+
+/* The wstr command writes the PXL buffer into a file. Under VMS, the file
+has stream record format. This is the best format for storing TeX-style
+"binary" files under VMS. */
+
+int handle_wstr()
+{
+
+ int strf;
+ char fs[FILESPECLEN];
+ int jnam,jext;
+ unsigned long i,target;
+
+ if (pxllen == 0) {
+ printf("\n There is no PXL file in memory.");
+ return(1);
+ }
+
+ skipb; istart = ilp; skipnb;
+ inline[ilp] = '\0';
+
+ strcpy(fs,&inline[istart]);
+ find_filename(fs,&jnam,&jext);
+ if (fs[jext] == '\0') strcat(fs,".pxl");
+ strf = creat(fs,0700);
+ if (strf == -1) {
+ printf("\nUnable to open PXL file %s",&inline[istart]);
+ return(1);
+ }
+ for (i=0; i<pxllen; i+=512) {
+ target = pxllen-i;
+ if (target > 512) target = 512;
+ if (write(strf,&pxlbuf[i],target) == -1) {
+ printf("\nError writing PXL file.");
+ close(strf);
+ return(1);
+ }
+ }
+
+ close(strf);
+ return(0);
+}
+
+int open_output_file (s,ext)
+char s[], ext[];
+{
+ char fs[FILESPECLEN];
+ int jnam,jext;
+
+ strcpy(fs,s);
+ find_filename(fs,&jnam,&jext);
+ if (fs[jext] == '\0') strcat(fs,ext);
+#ifdef vms
+ return(creat(fs,0,"rfm=fix","mrs=512"));
+#else
+ return(creat(fs,0700));
+#endif
+
+}
+
diff --git a/dviware/ln03/rose/older_driver.mem b/dviware/ln03/rose/older_driver.mem
new file mode 100644
index 0000000000..c1350ea850
--- /dev/null
+++ b/dviware/ln03/rose/older_driver.mem
@@ -0,0 +1,84 @@
+AN OLDER DRIVER (NOW OBSOLETE)
+
+If you are running under an old version of VMS (before 4.2),
+you may not be able to get dvi2ln3 to work because you don't
+have the C run time library. In that case, as a backup, we
+supply an older driver, which is considerably slower than
+dvi2ln3.
+
+The older driver is supplied as
+
+ dvi2lng.web, dvi2lng.ch, dvi2lng.obj
+ ln03topp.pli, ln03topp.obj, toppnft.pli, toppnft.obj,
+ lnfinc.pli, lnfinc.obj
+ ffc.web, ffc.ch, ffc.obj
+
+To get executables, relink:
+
+ $ link dvi2lng
+ $ link ln03topp,toppnft,lnfinc
+ $ link ffc
+
+The old driver doesn't read PXL files, so all PXL files have
+to be converted to the TEX$LN03DIR format files by the
+following recipe:
+
+ $ run ffc
+
+ Font File Converter
+
+ FFC> rpxl foo.yyyyPXL
+ FFC> toln03x
+ FFC> wln03 foo.xxx
+ FFC> exit
+ $
+
+Here yyyy/15 = xxx, and xxx% is the magnification of the
+font. If the font is not magnified, so that yyyy = 1500,
+then xxx must be NFT.
+
+The way to invoke the driver is different, also. You define
+two VMS symbols
+
+ $ dvi2lng == "$disk:[directory]dvi2lng"
+ $ ln03topp == "$disk:[directory]ln03topp"
+
+To convert foo.dvi to foo.ln3, you say
+
+ $ dvi2lng foo
+ $ ln03topp foo
+
+In addition to foo.ln3, this creates the useless files
+foo.typ, foo.lnh and foo.lng which you should delete.
+
+The Dvi2lng program takes the options /N and /S of the
+Dvi2ln3 program. The Ln03Topp program takes the options /H
+and /V of the Dvi2ln3 program. There is no option for
+printing in landscape.
+
+If you need to relink, the commands are as follows:
+
+ $ link dvi2lng
+ $ link ln03topp,toppnft,lnfinc
+
+The FFC commands are:
+
+ rpxl <file> Read pxl file
+ rln03 <file> Read ln03 font file
+ tln03 <character> Type the rasters for LN03 character, specified
+ as a decimal ascii code, or as the character
+ itself preceded by a single quote '
+ tpxl <character> Type the rasters for PXL character
+ toln03 Convert PXL file to LN03 format, converting
+ only the 94 characters with codes 33-126. The
+ converted file is held in memory, and must be
+ written out with a separate wln03 command.
+ toln03x Convert PXL file to LN03 font file, converting
+ all 128 characters
+ wln03 <filename> Write an LN03 file
+ tln03long <offset> These commands type longwords or words from the PXL
+ tln03word <offset> and LN03 files last read in, or the versions
+ tpxllong <offset> generated via the conversion command. The <offset>
+ tpxlword <offset> is a decimal number of bytes from the beginning of
+ the file.
+ exit Leave the FFC program
diff --git a/dviware/ln03/rose/picmode.pli b/dviware/ln03/rose/picmode.pli
new file mode 100644
index 0000000000..4169256a7c
--- /dev/null
+++ b/dviware/ln03/rose/picmode.pli
@@ -0,0 +1,529 @@
+%REPLACE false BY '0'B;
+%REPLACE true BY '1'B;
+
+/*
+
+The purpose of this program is to provide a standalone version of LNF's
+PICmode, outputting a TeX file which can be \input into a TeX document.
+Copyright (c) 1984 by Digital Equipment Corporation. Author: Flavio
+Rose. UNDER DEVELOPMENT.
+
+*/
+
+picmode: PROCEDURE OPTIONS(MAIN);
+
+ DCL (strip,frontstrip) EXTERNAL ENTRY (CHAR (*))
+ RETURNS (CHAR (*)),
+ cvis EXTERNAL ENTRY (FIXED) RETURNS (CHAR (*));
+ DCL upcase EXTERNAL ENTRY (CHAR (*)) RETURNS (CHAR (*));
+ DCL read_wln_file EXTERNAL ENTRY(FIXED,CHAR(*),FILE) RETURNS(BIT);
+
+ /* Debugging info */
+ DCL debug FILE;
+ DCL debugwid FIXED;
+ DCL debugflag BIT;
+ debugflag = false;
+ debugwid = 0;
+
+ /* input file, output file, wln file, include file */
+ DCL (outf,inf) FILE;
+ DCL (infnam,outfnam) CHAR(150) VARYING;
+
+ ON UNDEFINEDFILE (inf) BEGIN;
+ PUT SKIP LIST('No such file: ' || infnam);
+ GOTO terminal_eof;
+ END;
+
+ ON UNDEFINEDFILE (outf) BEGIN;
+ PUT SKIP LIST('Couldn''t open: ' || outfnam);
+ GOTO terminal_eof;
+ END;
+
+ /* Define <esc> <tab> as constants. In general this source file should
+ not contain any ascii control characters; instead we use byte(). */
+ DCL (esc,tab) CHAR(1);
+ esc = byte(27);
+ tab = byte(9);
+
+ /* Heading */
+ PUT SKIP LIST ('PICmode 1.0');
+ /* Hack command line */
+ %REPLACE obufsize BY 512;
+ %REPLACE inbufsize BY 512;
+ DCL ob CHAR(obufsize);
+ DCL inline CHAR(inbufsize) VARYING;
+ DCL tch CHAR(1);
+ DCL (illen,obp,ilp,i,istart,zch) FIXED;
+
+ %INCLUDE $stsdef;
+ DCL lib$get_foreign ENTRY (CHAR(*) VARYING, CHAR(*) VARYING,
+ FIXED(15),FIXED) OPTIONS(VARIABLE) RETURNS(FIXED);
+ DCL rms$_eof FIXED GLOBALREF VALUE;
+ DCL defdir CHAR(150) VARYING;
+ DCL (jj,jdir,jext) FIXED;
+
+ sts$value = lib$get_foreign(inline,,,0);
+
+ IF ^sts$success THEN DO;
+ PUT SKIP EDIT ('Usage: picmode <filename>') (A);
+ GOTO terminal_eof;
+ END;
+
+ inline = inline || ' ';
+ illen = length(inline);
+ ilp = 1;
+ CALL skipb;
+ IF ilp > illen THEN DO;
+ PUT SKIP EDIT ('Usage: picmode <filename>') (A);
+ GOTO terminal_eof;
+ END;
+ istart = ilp;
+ CALL skipnb2;
+ infnam = substr(inline,istart,ilp-istart);
+ OPEN FILE(inf) TITLE(infnam)
+ ENVIRONMENT(Default_file_name('FOO.PMD'));
+
+ jdir = istart;
+ DO jj = ilp-1 TO istart BY -1;
+ IF substr(inline,jj,1) = ':' |
+ substr(inline,jj,1) = ']' |
+ substr(inline,jj,1) = '>'
+ THEN DO;
+ jdir = jj+1;
+ GOTO found_jdir;
+ END;
+ END;
+
+found_jdir:
+ defdir = substr(inline,istart,jdir-istart);
+ jext = ilp;
+ DO jj = jdir TO ilp-1;
+ IF substr(inline,jj,1) = '.' |
+ substr(inline,jj,1) = ';'
+ THEN DO;
+ jext = jj;
+ GOTO found_jext;
+ END;
+ END;
+
+found_jext:
+ outfnam = substr(inline,jdir,jext-jdir) || '.TEX';
+
+ OPEN FILE(outf) OUTPUT TITLE(outfnam)
+ ENVIRONMENT(Default_file_name('FOO.TEX'));
+
+/* Globals:
+
+ font information
+ picmode uses just one font
+ but we use these for compatibility with LNFWLN */
+
+ DCL curf FIXED;
+ DCL (firstch(0:9),lastch(0:9),fht(0:9)) FIXED GLOBALDEF;
+ DCL chw(0:9,0:255) FIXED GLOBALDEF;
+ DCL fname(0:9) CHAR(20) GLOBALDEF;
+ DCL font_present(0:9) BIT GLOBALDEF;
+
+ curf = 0;
+ chw = 0;
+ font_present = false;
+
+ /* character classification for PIC mode */
+ DCL ztyp(0:255) FIXED(7);
+ ztyp = 0;
+ ztyp(rank('+')) = 1;
+ ztyp(rank('-')) = 2;
+ ztyp(rank('|')) = 3;
+ ztyp(rank('v')) = 4;
+ ztyp(rank('<')) = 5;
+ ztyp(rank('>')) = 6;
+ ztyp(rank('^')) = 7;
+ ztyp(rank('/')) = 8;
+ ztyp(rank('\')) = 9;
+ ztyp(rank('#')) = 10;
+ ztyp(rank('X')) = 11;
+
+ DCL horp(0:11) BIT ALIGNED INIT(false,true,true,false,
+ true,false,false,true,true,true,true,false);
+ DCL verp(0:11) BIT ALIGNED INIT(false,true,false,true,
+ false,true,true,false,true,true,true,false);
+ DCL arrowp(0:11) BIT ALIGNED INIT(false,false,false,false,
+ true,true,true,true,false,false,false,false);
+ DCL slant(0:11) BIT ALIGNED INIT(false,false,false,false,
+ false,true,true,false,true,true,false,true);
+ PUT EDIT ('\vbox{\font\amgr=AMGR at 10truept\baselineskip0.1466667truein' ||
+ '\lineskiplimit-\maxdimen') (A) FILE(outf);
+ PUT SKIP EDIT ('\catcode`\-=\active\catcode`\~=\active' ||
+ '\def~{{\char32}}\def-{{\char1}}%') (A) FILE(outf);
+ CALL process_pic;
+ PUT EDIT ('}') (A) FILE(outf);
+
+abort_job:
+ CLOSE FILE(inf);
+
+
+terminal_eof:
+ RETURN;
+
+ process_pic: PROCEDURE;
+ DCL ils(3) CHAR(inbufsize) VARYING;
+ DCL (temp,prev,curr,next,quo,rem) FIXED;
+ DCL last_line BIT;
+
+ ils(1) = '';
+ last_line = false;
+ prev = 1;
+ curr = 2;
+ next = 3;
+ IF ^read_line_for_pic(2) THEN RETURN; /* pic is empty */
+ IF ^read_line_for_pic(3) THEN last_line = true;
+ DO WHILE (true);
+ CALL output_pic_line;
+ IF last_line THEN DO;
+ obp = 0;
+ RETURN;
+ END;
+ temp = prev;
+ prev = curr;
+ curr = next;
+ next = temp;
+ IF ^read_line_for_pic(next) THEN last_line = true;
+ END;
+
+ read_line_for_pic: PROCEDURE (which) RETURNS (BIT);
+ DCL (which,true_ilp) FIXED;
+
+ ON ENDFILE (inf) GOTO end_of_inf;
+
+ ils(which) = '';
+ GET EDIT (inline) (A(511)) FILE(inf);
+
+ inline = inline || ' ';
+ illen = length(inline);
+ /* For PICs, we attempt to untabify input lines */
+ true_ilp = 1;
+ DO ilp = 1 TO illen-1;
+ IF substr(inline,ilp,1) ^= tab
+ THEN DO;
+ ils(which) = ils(which) || substr(inline,ilp,1);
+ true_ilp = true_ilp+1;
+ END;
+ ELSE DO;
+ quo = DIVIDE(true_ilp-1,8,31);
+ rem = true_ilp-1-quo*8;
+ ils(which) = ils(which) || copy(' ',8-rem);
+ true_ilp = true_ilp+8-rem;
+ END;
+ END;
+ RETURN(true);
+
+ end_of_inf:
+ CLOSE FILE(inf);
+ RETURN(false);
+
+ END read_line_for_pic;
+
+ output_pic_line: PROCEDURE;
+ DCL (iz,prevl,currl,nextl,icnt,nbors(8)) FIXED;
+ DCL (zch,tch) CHAR;
+ DCL cnt_to_char_plus(0:15) FIXED STATIC INIT(
+ 43,161,160,164,161,161,165,177,
+ 160,163,161,176,162,178,175,174);
+ DCL cnt_to_char_sharp(0:15) FIXED STATIC INIT(
+ 35,161,160,186,161,161,187,177,
+ 160,185,161,176,184,178,175,174);
+ DCL (xtra1,xtra2) CHAR(inbufsize) VARYING;
+ DCL (xtra1used,xtra2used,present(8)) BIT ALIGNED;
+
+ xtra1used = false;
+ xtra2used = false;
+ xtra1 = '';
+ xtra2 = '';
+ obp = 0;
+ prevl = length(ils(prev));
+ currl = length(ils(curr));
+ nextl = length(ils(next));
+ DO iz = 1 TO currl;
+ zch = substr(ils(curr),iz);
+ IF ztyp(rank(zch)) ^= 0 THEN CALL det_nbors;
+ IF ztyp(rank(zch)) = 0 THEN tch = zch;
+ ELSE IF zch = '-' THEN DO;
+ IF ztyp(nbors(1)) ^= 0 | ztyp(nbors(3)) ^= 0
+ THEN tch = byte(161);
+ ELSE tch = '-';
+ END; ELSE IF zch = '|' THEN DO;
+ IF ztyp(nbors(2)) ^= 0 | ztyp(nbors(4)) ^= 0
+ THEN tch = byte(160);
+ ELSE tch = '|';
+ END; ELSE IF zch = '+' THEN DO;
+ icnt = 0;
+ IF horp(ztyp(nbors(1))) THEN icnt = icnt+1;
+ IF verp(ztyp(nbors(2))) THEN icnt = icnt+2;
+ IF horp(ztyp(nbors(3))) THEN icnt = icnt+4;
+ IF verp(ztyp(nbors(4))) THEN icnt = icnt+8;
+ IF nbors(1) = rank('<') THEN DO;
+ IF verp(ztyp(nbors(5))) | verp(ztyp(nbors(8)))
+ THEN icnt = icnt+1;
+ END;
+ IF nbors(2) = rank('^') THEN DO;
+ IF horp(ztyp(nbors(5))) | horp(ztyp(nbors(6)))
+ THEN icnt = icnt+2;
+ END;
+ IF nbors(3) = rank('>') THEN DO;
+ IF verp(ztyp(nbors(6))) | verp(ztyp(nbors(7)))
+ THEN icnt = icnt+4;
+ END;
+ IF nbors(4) = rank('v') THEN DO;
+ IF horp(ztyp(nbors(7))) | horp(ztyp(nbors(8)))
+ THEN icnt = icnt+8;
+ END;
+ tch = byte(cnt_to_char_plus(icnt));
+ END; ELSE IF zch = 'v' | zch = '^' THEN DO;
+ IF (horp(ztyp(nbors(1))) &
+ ^arrowp(ztyp(nbors(1))))
+ | (horp(ztyp(nbors(3))) &
+ ^arrowp(ztyp(nbors(3))))
+ THEN DO;
+ IF zch = 'v' THEN tch = byte(181);
+ ELSE tch = byte(182);
+ END; ELSE tch = zch;
+ END; ELSE IF zch = '<' THEN DO;
+ IF (verp(ztyp(nbors(2))) &
+ ^arrowp(ztyp(nbors(2))))
+ | (verp(ztyp(nbors(4))) &
+ ^arrowp(ztyp(nbors(4))))
+ THEN tch = byte(179);
+ ELSE IF (nbors(6) = rank('/')
+ | nbors(6) = rank('>') | nbors(7) = rank('\')
+ | nbors(7) = rank('>'))
+ THEN DO;
+ tch = byte(173);
+ CALL superpose(byte(172));
+ IF horp(ztyp(nbors(1))) THEN
+ CALL superpose(byte(167));
+ END; ELSE tch = zch;
+ END; ELSE IF zch = '>' THEN DO;
+ IF (verp(ztyp(nbors(2))) &
+ ^arrowp(ztyp(nbors(2))))
+ | (verp(ztyp(nbors(4))) &
+ ^arrowp(ztyp(nbors(4))))
+ THEN tch = byte(180);
+ ELSE IF (nbors(8) = rank('/')
+ | nbors(5) = rank('<') | nbors(5) = rank('\')
+ | nbors(8) = rank('<'))
+ THEN DO;
+ tch = byte(170);
+ CALL superpose(byte(171));
+ IF horp(ztyp(nbors(3))) THEN
+ CALL superpose(byte(169));
+ END; ELSE tch = zch;
+ END; ELSE IF zch = '/' THEN DO;
+ present = false;
+ IF nbors(6) = rank('/') | nbors(6) = rank('>')
+ | nbors(6) = rank('X') | nbors(3) = rank('\')
+ THEN present(6) = true;
+ IF nbors(8) = rank('/') | nbors(8) = rank('<')
+ | nbors(8) = rank('X') | nbors(1) = rank('\')
+ THEN present(8) = true;
+ IF ^(present(6) | present(8)) THEN DO;
+ tch = zch;
+ GOTO break;
+ END;
+ IF present(6) & present(8) THEN tch = byte(188);
+ ELSE IF present(6) THEN tch = byte(172);
+ ELSE IF present(8) THEN tch = byte(170);
+ IF horp(ztyp(nbors(1))) & ^slant(ztyp(nbors(1)))
+ THEN present(1) = true;
+ IF horp(ztyp(nbors(3))) & ^slant(ztyp(nbors(3)))
+ THEN present(3) = true;
+ IF present(1) & present(3) THEN CALL superpose(
+ byte(161));
+ ELSE IF present(1) THEN CALL superpose(byte(167));
+ ELSE IF present(3) THEN CALL superpose(byte(169));
+ END; ELSE IF zch = '\' THEN DO;
+ present = false;
+ IF nbors(5) = rank('\') | nbors(5) = rank('<')
+ | nbors(5) = rank('X') | nbors(3) = rank('/')
+ THEN present(5) = true;
+ IF nbors(7) = rank('\') | nbors(7) = rank('>')
+ | nbors(7) = rank('X') | nbors(1) = rank('/')
+ THEN present(7) = true;
+ IF ^(present(5) | present(7)) THEN DO;
+ tch = zch;
+ GOTO break;
+ END;
+ IF horp(ztyp(nbors(1))) & ^slant(ztyp(nbors(1)))
+ THEN present(1) = true;
+ IF horp(ztyp(nbors(3))) & ^slant(ztyp(nbors(3)))
+ THEN present(3) = true;
+ IF present(5) & present(7) THEN tch = byte(189);
+ ELSE IF present(5) THEN tch = byte(171);
+ ELSE IF present(7) THEN tch = byte(173);
+ IF present(1) & present(3) THEN CALL superpose(
+ byte(161));
+ ELSE IF present(1) THEN CALL superpose(byte(167));
+ ELSE IF present(3) THEN CALL superpose(byte(169));
+ END; ELSE IF zch = '#' THEN DO;
+ icnt = 0;
+ IF horp(ztyp(nbors(1))) THEN icnt = icnt+1;
+ IF verp(ztyp(nbors(2))) THEN icnt = icnt+2;
+ IF horp(ztyp(nbors(3))) THEN icnt = icnt+4;
+ IF verp(ztyp(nbors(4))) THEN icnt = icnt+8;
+ IF nbors(1) = rank('<') THEN DO;
+ IF verp(ztyp(nbors(5))) | verp(ztyp(nbors(8)))
+ THEN icnt = icnt+1;
+ END;
+ IF nbors(2) = rank('^') THEN DO;
+ IF horp(ztyp(nbors(5))) | horp(ztyp(nbors(6)))
+ THEN icnt = icnt+2;
+ END;
+ IF nbors(3) = rank('>') THEN DO;
+ IF verp(ztyp(nbors(6))) | verp(ztyp(nbors(7)))
+ THEN icnt = icnt+4;
+ END;
+ IF nbors(4) = rank('v') THEN DO;
+ IF horp(ztyp(nbors(7))) | horp(ztyp(nbors(8)))
+ THEN icnt = icnt+8;
+ END;
+ tch = byte(cnt_to_char_sharp(icnt));
+ END; ELSE IF zch = 'X' THEN DO;
+ IF nbors(5) = rank('\') & nbors(6) = rank('/') &
+ nbors(7) = rank('\') & nbors(8) = rank('/')
+ THEN DO;
+ tch = byte(188);
+ CALL superpose(byte(189));
+ END; ELSE tch = zch;
+ END; ELSE tch = zch;
+ break:
+ obp = obp+1;
+ substr(ob,obp,1) = tch;
+ END;
+
+ IF xtra1used THEN PUT SKIP EDIT ('\hbox{\rlap{%') (A) FILE(outf);
+ CALL really_output(substr(ob,1,obp));
+ IF xtra1used THEN DO;
+ PUT EDIT ('}%') (A) FILE(outf);
+ IF xtra2used THEN PUT SKIP EDIT ('\rlap{%') (A) FILE(outf);
+ CALL really_output(xtra1);
+ IF xtra2used THEN DO;
+ PUT EDIT ('}%') (A) FILE(outf);
+ CALL really_output(xtra2);
+ END;
+ PUT EDIT ('}') (A) FILE(outf);
+ END;
+
+ det_nbors: PROCEDURE;
+ DCL space_rank FIXED;
+
+ space_rank = rank(' ');
+ IF iz = 1 THEN nbors(1) = space_rank;
+ ELSE nbors(1) = rank(substr(ils(curr),iz-1,1));
+ IF iz = 1 | iz-1 > prevl THEN nbors(5) = space_rank;
+ ELSE nbors(5) = rank(substr(ils(prev),iz-1,1));
+ IF iz > prevl THEN nbors(2) = space_rank;
+ ELSE nbors(2) = rank(substr(ils(prev),iz,1));
+ IF iz+1 > prevl THEN nbors(6) = space_rank;
+ ELSE nbors(6) = rank(substr(ils(prev),iz+1,1));
+ IF iz = currl THEN nbors(3) = space_rank;
+ ELSE nbors(3) = rank(substr(ils(curr),iz+1,1));
+ IF iz = 1 | iz-1 > nextl THEN nbors(8) = space_rank;
+ ELSE nbors(8) = rank(substr(ils(next),iz-1,1));
+ IF iz > nextl THEN nbors(4) = space_rank;
+ ELSE nbors(4) = rank(substr(ils(next),iz,1));
+ IF iz+1 > nextl THEN nbors(7) = space_rank;
+ ELSE nbors(7) = rank(substr(ils(next),iz+1,1));
+
+ END det_nbors;
+
+ superpose: PROCEDURE (x);
+ DCL x CHAR;
+
+ IF ^xtra1used THEN DO;
+ xtra1 = copy(' ',obp+1);
+ substr(xtra1,obp+1,1) = x;
+ xtra1used = true;
+ END; ELSE IF length(xtra1) < obp+1
+ THEN xtra1 = xtra1 || copy(' ',obp-length(xtra1)) || x;
+ ELSE IF substr(xtra1,obp+1,1) = ' '
+ THEN substr(xtra1,obp+1,1) = x;
+ ELSE IF ^xtra2used THEN DO;
+ xtra2 = copy(' ',obp+1);
+ substr(xtra2,obp+1,1) = x;
+ xtra2used = true;
+ END; ELSE IF length(xtra2) < obp+1
+ THEN xtra2 = xtra2 || copy(' ',obp-length(xtra2)) || x;
+ ELSE substr(xtra2,obp+1,1) = x;
+
+ END superpose;
+
+ really_output: PROCEDURE (who);
+
+ DCL who CHAR(*) VARYING;
+ DCL (i,j,k) FIXED;
+ DCL (tch,tcat) CHAR(1);
+ DCL cats(32:126) CHAR(1) STATIC INIT
+ ('e',(2)('o'),(4)('e'),(6)('o'),'e',(46)('o'),
+ 'e','o',(2)('e'),(27)('o'),'e','o',(2)('e'));
+
+ j = length(who);
+
+ PUT SKIP EDIT ('\hbox{\amgr ') (A) FILE(outf);
+ k = 0;
+ DO i = 1 to j;
+ tch = substr(who,i,1);
+ IF rank(tch) < 32 | (rank(tch) > 126 &
+ rank(tch) < 160)
+ THEN tch = ' ';
+ k = k+1;
+ IF tch = ' ' THEN PUT EDIT ('~') (A) FILE(outf);
+ ELSE IF tch = byte(161) THEN PUT EDIT ('-') (A) FILE(outf);
+ ELSE IF rank(tch) >= 160 THEN DO;
+ PUT EDIT ('{\char' || cvis(rank(tch)-160) || '}')
+ (A) FILE(outf);
+ k = k+7;
+ END; ELSE DO;
+ tcat = cats(rank(tch));
+ IF tcat = 'o' THEN PUT EDIT (tch) (A) FILE(outf);
+ ELSE DO;
+ PUT EDIT ('{\char' || cvis(rank(tch)) || '}')
+ (A) FILE(outf);
+ k = k+7;
+ END;
+ END;
+ IF k > 100 THEN DO;
+ PUT EDIT ('%') (A) FILE(outf);
+ PUT SKIP FILE(outf);
+ k = 0;
+ END;
+
+ END;
+ PUT EDIT ('}') (A) FILE(outf);
+
+ END really_output;
+
+ END output_pic_line;
+
+ END process_pic;
+
+ /* skip over blanks in infile */
+ skipb: PROCEDURE;
+
+ DO WHILE (ilp <= illen);
+ IF substr(inline,ilp,1) ^= ' ' THEN RETURN;
+ ilp = ilp+1;
+ END;
+
+ END skipb;
+
+ /* skip over characters other than ' ' and '}'. */
+ skipnb2: PROCEDURE;
+
+ DO WHILE (ilp <= illen);
+ IF substr(inline,ilp,1) = ' '
+ | substr(inline,ilp,1) = '}' THEN RETURN;
+ ilp = ilp+1;
+ END;
+
+ END skipnb2;
+
+END picmode;
diff --git a/dviware/ln03/rose/specials.c b/dviware/ln03/rose/specials.c
new file mode 100644
index 0000000000..1cfb0bb471
--- /dev/null
+++ b/dviware/ln03/rose/specials.c
@@ -0,0 +1,480 @@
+/* This file contains the code that implements the handling of \special's
+in Dvi2ln3.
+
+Copyright (c) 1985 by Digital Equipment Corporation. Author: Flavio Rose,
+(617) 467-7545, ...ucbvax!decwrl!dec-rhea!dec-dvinci!rose.
+
+Right now, the following \special's are implemented:
+
+ln03:defpoint fixnum ( [dimension] , [dimension] )
+ln03:connect fixnum [/ fixnum] fixnum [/ fixnum]
+ln03:plotfile filename
+ln03:resetpoints fixnum fixnum
+
+A dimension is a number with optional decimal point, followed by one of pt,
+in, pc, cm, mm, bp, dd, cc, and mi.
+
+We try to mimic the functionality of DVIAPS, a program that drives the
+Autologic Micro-5 typesetter, written by Textset Inc. of Ann Arbor,
+Michigan. This has been done on the basis of user-level documentation
+supplied by Textset. This code does not contain any Textset-proprietary
+information.
+
+\special's are parsed case-insensitively.
+
+Revision history:
+
+10/8/85 Created file, wrote, tested code for ln03:plotfile
+
+10/16/85 ln03:resetpoints, ln03:defpoint, ln03:connect
+
+12/12/85 Corrected bug: ln03:plotfile does not update LN03's
+ position to the latest from the DVI file.
+
+*/
+
+#ifdef vms
+#include stdio
+#include ctype
+#include math
+#include file
+#else
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#endif
+
+/* In VMS we declare external variables to be globalref.
+This is not really necessary, just an old habit. */
+
+#ifdef vms
+#define GLOBAL globaldef
+#define EXTERN globalref
+#else
+#define GLOBAL
+#define EXTERN extern
+#endif
+
+#define NUMSPECIALS 4
+
+GLOBAL char *specialnames[NUMSPECIALS] = { "defpoint",
+ "connect", "plotfile", "resetpoints" } ;
+
+EXTERN FILE *dvifile,*outfile;
+
+/* There are two top-level routines for handling specials, one to be
+invoked during pass1, the other to be invoked during pass2. The parameter
+that is passed is the size of the \special string. */
+
+int do_special_pass1 (p)
+int p;
+{
+ for (; p != 0; p--) getc(dvifile);
+ return(0);
+}
+
+/* Specials get put in a buffer for easier parsing. The buffer is a little
+longer than needed to make some things simpler. [[This ought to be
+cleaner.]] */
+
+#define MAXSPECIAL 1000
+
+GLOBAL char sb[MAXSPECIAL+7];
+GLOBAL int sbp,cstart;
+
+/* Do_special_pass2 gets called from the main program to perform special
+processing in pass2, the pass in which stuff actually gets written out to
+the LN3 output file. The parameter p is the length of the special. */
+
+int do_special_pass2 (p)
+int p;
+{
+ char c;
+ int i,j;
+
+/* Skip whitespace in input */
+
+ sbp = 0;
+ while (p != 0) {
+ c = getc(dvifile);
+ p--;
+ if (!isspace(c)) {
+ sb[sbp] = c;
+ sbp = 1;
+ break;
+ }
+ }
+ if (sbp == 0) return(1);
+
+/* Now we have non-whitespace, read the rest of the special, or as much as
+will fit in the buffer. Lowercase the stuff as it is read in. */
+
+ for (; p != 0; p--) {
+ sb[sbp] = tolower(getc(dvifile));
+ sbp++;
+ if (sbp > MAXSPECIAL) break;
+ }
+
+/* Put a null as sentinel at the end of the special. */
+
+ sb[sbp] = 0;
+
+/* Determine whether the special pertains to the ln03, in which case sbpf
+will contain "ln03" followed by whitespace or a colon. */
+
+ if (strncmp(sb,"ln03",sizeof("ln03")-1) != 0) return(1);
+ sbp = sizeof("ln03")-1;
+
+/* Skip whitespace and one colon */
+
+ while (isspace(sb[sbp])) sbp++;
+ if (sb[sbp] != ':') return(1);
+ sbp++;
+
+/* Now, it might be that the special was too long for the special buffer,
+so check that out and issue an error message. */
+
+ if (p != 0) {
+ printf("\n \special too long (over %d bytes).",MAXSPECIAL);
+ for (; p != 0; p--) getc(dvifile);
+ return(1);
+ }
+
+/* Now determine if the special command is one of those which the driver is
+supposed to recognize. As in the DVIAPS program, only the first six bytes
+of the command are significant, so plotfile could be written plotfi and
+defpoint could be written defpoi. */
+
+ while (isspace(sb[sbp])) sbp++;
+ cstart = sbp;
+ while (!isspace(sb[sbp]) && sb[sbp] != '\0') sbp++;
+ sb[sbp] = 0;
+ sbp++;
+
+ for (i = 0; i<NUMSPECIALS; i++)
+ if (strncmp(&sb[cstart],specialnames[i],6) == 0) break;
+
+ if (i == NUMSPECIALS) {
+ printf("\n Unrecognized option '%s' ignored in \special command",
+ &sb[cstart]);
+ return(1);
+ } else {
+ switch (i) {
+ case 0:
+ j = do_defpoint_pass2();
+ break;
+ case 1:
+ j = do_connect_pass2();
+ break;
+ case 2:
+ j = do_plotfile_pass2();
+ case 3:
+ j = do_resetpoints();
+ }
+ }
+}
+
+/* In certain \specials, dimensions may be specified in any of nine
+different units of measure. */
+
+#define MAXUNITS 9
+
+GLOBAL char *unit_array[MAXUNITS] = { "pt", "in", "pc", "cm", "mm", "bp",
+ "dd", "cc", "mi" };
+
+/* Unit_convert gives a floating point conversion factor from units to
+pixels. [[The factor for micas is doubtful.]] */
+
+#define PIXELS_PER_INCH (300.0)
+
+GLOBAL float unit_convert[MAXUNITS] = { PIXELS_PER_INCH/72.27,
+ PIXELS_PER_INCH, 12.0*PIXELS_PER_INCH/72.27,
+ PIXELS_PER_INCH/2.54, PIXELS_PER_INCH/25.4,
+ PIXELS_PER_INCH/72.0, (1238.0/1157.0)*PIXELS_PER_INCH/72.27,
+ 12.0*(1238.0/1157.0)*PIXELS_PER_INCH/72.27, PIXELS_PER_INCH/2540.0 };
+
+/* There are 255 point variables, numbered 1 through 255. */
+
+#define MAXPOINTS 255
+
+GLOBAL int point_hh[MAXPOINTS], point_vv[MAXPOINTS];
+GLOBAL int point_present[MAXPOINTS];
+
+GLOBAL char *special_mess = "\n Error in \special{ln03:%s...} parameters";
+
+/* For the purposes of defpoint, we need to know the current horizontal and
+vertical positions in pixels. */
+
+EXTERN int hh,vv,hoff,voff;
+
+/* ln03:defpoint fixnum ( [dimension] , [dimension] ) */
+
+int do_defpoint_pass2()
+{
+ int i,which,hh_val,vv_val;
+ float xx_p,yy_p;
+
+ which = 0;
+ if (scan_fixnum(&which) != 0 || which < 1 || which > MAXPOINTS) {
+ printf(special_mess,"defpoint");
+ printf("\n Invalid point number (%d)",which);
+ return(1);
+ }
+ while (isspace(sb[sbp]) || sb[sbp] == ',') sbp++;
+ hh_val = hh;
+ vv_val = vv;
+ if (sb[sbp] != '(') goto record_point;
+ sbp++;
+ while (isspace(sb[sbp])) sbp++;
+ if (sb[sbp] == '\0') goto record_point;
+ if (sb[sbp] == ',') sbp++;
+ else {
+ scan_dimension(&hh_val);
+ while (isspace(sb[sbp])) sbp++;
+ if (sb[sbp] == ',') sbp++;
+ else goto record_point;
+ }
+ while(isspace(sb[sbp])) sbp++;
+ if (sb[sbp] != ')' && sb[sbp] != '\0') scan_dimension(&vv_val);
+record_point:
+ point_hh[which-1] = hh_val;
+ point_vv[which-1] = vv_val;
+ point_present[which-1] = 1;
+ return(0);
+}
+
+EXTERN long first_counter;
+
+int do_connect_pass2()
+{
+ int a1,b1,a2,b2,w;
+
+ scan_xpoint(&a1,&b1);
+ while (isspace(sb[sbp]) || sb[sbp] == ',') sbp++;
+ scan_xpoint(&a2,&b2);
+ while (isspace(sb[sbp]) || sb[sbp] == ',') sbp++;
+ w = 2; /* default width is two pixels ~ 0.4 points */
+ if (sb[sbp] != '\0') scan_dimension(&w);
+
+ if (first_counter%2 == 0) connect_points(b1,b2,w);
+ else connect_points(a1,a2,w);
+
+ return(0);
+}
+
+EXTERN int ln3p,vpset,hh_old;
+
+int do_plotfile_pass2()
+{
+ char buf[512];
+ int i,j;
+ char c;
+
+/* Read filename into array. */
+
+ while (isspace(sb[sbp])) sbp++;
+ i = sbp;
+ while (!isspace(sb[sbp]) && sb[sbp] != '\0') sbp++;
+ sb[sbp] = 0;
+
+/* Try to open the file. [[What is the status of O_RDONLY under Unix? Is
+that a 4.2bsd hack that it would be best to leave out?]] */
+
+#ifdef vms
+ j = open(&sb[i],O_RDONLY,0);
+#else
+ j = open(&sb[i],0,0);
+#endif
+ if (j == -1) {
+ printf("\n Unable to open plotfile %s",&sb[i]);
+ return(1);
+ }
+
+/* In executing the plotfile special, always emit escape sequences to place
+the LN03 at the current position of the DVI file, even if it would seem
+that the LN03 is already there. We do this in order to be able to emit a
+newline at this point. Emitting a newline at this point keeps the line
+length of the .ln3 file to <= 16 characters more than the line length of
+the plotfile. */
+
+ fprintf(outfile,"\n\033[%dd\033[%d`",vv+voff,hh+hoff);
+
+/* Read stuff from the plotfile, write it to the output file. [[Could this
+cause problem with one record being split into two?]] */
+
+ while((i = read(j,buf,512)) > 0)
+ fwrite(buf,i,1,outfile);
+ close(j);
+
+/* Now, set the variables ln3p, vpset and hh_old to indicate to the caller
+that its recorded value of the current position of the LN03 is no longer
+correct. This will make the caller issue absolute positioning commands
+before doing any further output to the dvifile. */
+
+ ln3p = 0;
+ vpset = 0;
+ hh_old = 30000;
+ return(0);
+}
+
+
+/* Scan_fixnum reads an integer off sb[], starting at sbp and advancing sbp
+to the first character past a valid integer. The integer is returned in i.
+*/
+
+int scan_fixnum(i)
+long *i;
+{
+ int sbp_save;
+
+ sbp_save = sbp;
+ *i = atol(&sb[sbp]);
+ while(isspace(sb[sbp])) sbp++;
+ if (sb[sbp] == '+' || sb[sbp] == '-') sbp++;
+ while (isdigit(sb[sbp])) sbp++;
+ return(sbp == sbp_save);
+}
+
+
+/* Scan_flonum reads a flonum (without exponential notation) off sb[],
+starting at sbp and advancing sbp to the first character past a valid
+flonum. The flonum is returned in x. [[Unfortunately, it is impossible to
+use atof or sscanf to implement this function, because they don't allow
+flonums like ".3" which don't have any digits before the decimal point.]]
+*/
+
+int scan_flonum(x)
+float *x;
+{
+ float j,frac;
+ char negative;
+ int sbp_save;
+
+ sbp_save = sbp;
+ negative = 0;
+ j = 0.0;
+ while (isspace(sb[sbp])) sbp++;
+ if (sb[sbp] == '-') { negative = 1; sbp++; }
+ else if (sb[sbp] == '+') sbp++;
+ while (isdigit(sb[sbp])) {
+ j = 10.0*j + ((float)(sb[sbp] - '0'));
+ sbp++;
+ }
+ if (sb[sbp] == '.') {
+ sbp++;
+ frac = 0.1;
+ while (isdigit(sb[sbp])) {
+ j += frac*((float)(sb[sbp] - '0'));
+ frac *= 0.1;
+ sbp++;
+ }
+ }
+ if (sbp != sbp_save) {
+ *x = negative ? (-j) : j;
+ return(0);
+ } else return(1);
+
+}
+
+
+/* Scan_dimension tries to parse a dimension off sb[], advancing sbp over
+what it can parse. The dimension is converted to pixels and returned in
+val. If the unit isn't recognizable, scan_dimension prints an error
+message, returns 1 and leaves val unchanged. */
+
+int scan_dimension(val)
+int *val;
+{
+ float x;
+ int i;
+
+ x = 0.0;
+ scan_flonum(&x);
+ while (isspace(sb[sbp])) sbp++;
+ for (i = 0; i<MAXUNITS; i++)
+ if (strncmp(&sb[sbp],unit_array[i],2) == 0) break;
+ if (i == MAXUNITS) {
+ printf(special_mess,&sb[cstart]);
+ printf("\n No such unit of measure: %.2s",&sb[sbp]);
+ return(1);
+ }
+ sbp += 2;
+ x *= unit_convert[i];
+#ifdef vms
+ *val = (x < 0) ? floor(x-0.5) : floor(x+0.5);
+#else
+ *val = (x < 0) ? (x-0.5) : (x+0.5);
+#endif
+ return(0);
+}
+
+/* Scan_xpoint picks an xpoint off sb[], advancing sbp as usual. If both
+parts are present they are returned in a,b; if only one is, it's returned
+in both a and b. */
+
+int scan_xpoint(a,b)
+int *a,*b;
+{
+
+ *a = 0; *b = 0;
+ scan_fixnum(a);
+ while (isspace(sb[sbp])) sbp++;
+ if (sb[sbp] == '/') {
+ sbp++;
+ scan_fixnum(b);
+ } else *b = *a;
+ return(0);
+}
+
+/* int do_rule(xx0,yy0,xx1,yy1) */
+
+int connect_points(b1,b2,w)
+int b1,b2;
+int w;
+{
+ int halfw;
+
+
+ if (b1 < 1 || b1 > MAXPOINTS || !point_present[b1-1]) {
+ printf(special_mess,&sb[cstart]);
+ printf("\n Invalid point number (%d)",b1);
+ return(1);
+ }
+ if (b2 < 1 || b2 > MAXPOINTS || !point_present[b2-1]) {
+ printf(special_mess,&sb[cstart]);
+ printf("\n Invalid point number (%d)",b2);
+ return(1);
+ }
+
+ halfw = w/2;
+ if (point_hh[b1-1] == point_hh[b2-1])
+ do_rule(point_hh[b1-1]-halfw,
+ point_vv[b1-1],point_hh[b1-1]+w-1-halfw,
+ point_vv[b2-1]);
+ else if (point_vv[b1-1] == point_vv[b2-1])
+ do_rule(point_hh[b1-1],
+ point_vv[b1-1]-halfw,point_hh[b2-1],
+ point_vv[b1-1]+w-1-halfw);
+ else {
+ printf(special_mess,&sb[cstart]);
+ printf("\n Can't connect along a diagonal.");
+ return(1);
+ }
+ return(0);
+}
+
+int do_resetpoints ()
+{
+ int a,b;
+
+ a = 0;
+ scan_fixnum(&a);
+ b = 0;
+ scan_fixnum(&b);
+
+ if (0 < a && a <= b && b <= MAXPOINTS)
+ for (; a <= b; a++) point_present[a] = 0;
+ else if (0 < a && a <= MAXPOINTS)
+ point_present[a] = 0;
+ return(0);
+}
diff --git a/dviware/ln03/rose/toppnft.pli b/dviware/ln03/rose/toppnft.pli
new file mode 100644
index 0000000000..0ea9b4dd53
--- /dev/null
+++ b/dviware/ln03/rose/toppnft.pli
@@ -0,0 +1,654 @@
+%REPLACE true BY '1'B;
+%REPLACE false BY '0'B;
+%REPLACE maxnfonts BY 21;
+%REPLACE maxtexfonts BY 100;
+%REPLACE nblanks BY 0;
+%REPLACE leftfirst BY 33;
+%REPLACE leftlast BY 126;
+%REPLACE rightfirst BY 161;
+%REPLACE rightlast BY 254;
+
+/* This file contains those portions of LN03Topp that deal directly with
+the font file format (.NFT files). It is under development...
+
+Development chronology:
+
+11/28/84: Initial version. Wrote COPY_CHAR.
+
+11/29/84: GOOD_OPENING. Second thoughts about whole interface.
+ Merge the three functions into one.
+
+Copyright (c) 1983, 1984 by Digital Equipment Corporation. */
+
+/* The purpose of ADD_TXF_TO_LNF is to read an NFT file corresponding to
+TeX font TXFNO and place its rasters into the output font buffer. It
+returns the number of bytes by which the NFT file incremented the size of
+the output file, or a negative value to indicate lossage. */
+
+add_txf_to_lnf: PROCEDURE(lnfno,txfno,nftfname,outf) RETURNS(FIXED);
+
+ DCL nftfname CHAR(*);
+ DCL (txfno,lnfno) FIXED;
+ DCL outf FILE;
+
+ DCL (strip,frontstrip) EXTERNAL ENTRY (CHAR (*))
+ RETURNS (CHAR (*)),
+ cvis EXTERNAL ENTRY (FIXED) RETURNS (CHAR (*));
+
+ DCL lastch(0:maxnfonts) FIXED GLOBALREF;
+ DCL chw(0:maxnfonts,0:255) FIXED GLOBALREF;
+
+ DCL 1 txf BASED,
+ 2 nchs FIXED,
+ 2 chu(0:255) CHAR(1);
+ DCL txfa(0:maxtexfonts) PTR GLOBALREF;
+
+/* The buffer for the output fonts is currently of fixed size. Eventually
+it should be possible to chain a lot of these together so output fonts of
+unlimited size can be handled, subject only to virtual memory availability.
+*/
+
+ %REPLACE obuflen BY 131120;
+ DCL lnf(0:obuflen) CHAR(1) STATIC;
+ DCL (ras_len,psize,lsize,msize) FIXED STATIC;
+ DCL using_r BIT STATIC;
+
+/* Out of laziness, we read the entire input NFT file into a big array and
+then copy it. Obviously this is foolish; we should read it block by block
+only, and even use "random reading" if the RMS file type is suitable. */
+
+ DCL nfta(0:obuflen) CHAR(1);
+ DCL (i,j,k,nftlen) FIXED;
+
+ IF txfno < 0 | txfno > maxtexfonts | txfa(txfno) = Null() THEN
+ GOTO error_return;
+ IF lnfno < 0 | lnfno > maxnfonts THEN GOTO error_return;
+
+ PUT SKIP EDIT ('Loading font ' || nftfname) (A);
+
+ nftlen = 0;
+ CALL read_into_nfta;
+
+/* The NFT file comes to us in bytes. We need ways to understand it as
+words or longwords. When we need to parse some bytes as something, we copy
+them into the following overlaid variables. Note that unsigned quantities
+still require fixing. */
+
+ DCL longword FIXED;
+ DCL chars CHAR(4) BASED (Addr(longword));
+
+/* We make exactly one gratuitous test of NFT validity: the second longword
+must be 'FONT'. */
+
+ IF nfta(4) ^= 'F' | nfta(5) ^= 'O' | nfta(6) ^= 'N' | nfta(7) ^= 'T'
+ THEN GOTO bad_format;
+
+/* We also have to check that there are a full 16 bytes of character
+parameters. If there are fewer, we give up: it's too hard to figure out how
+to give the missing ones reasonable values. Maybe in the next version... */
+
+ %REPLACE fnt$l_parameters_size BY 200;
+ CALL nft_copy(fnt$l_parameters_size);
+ IF longword ^= 16 THEN GOTO bad_format;
+
+/* Figure out where the character directory in the NFT file begins */
+
+ %REPLACE fnt$a_char_directory BY 120;
+ DCL chardir_offset FIXED;
+
+/* CHARDIR_OFFSET should always equal 480. But we read it off the file
+anyway. */
+
+ CALL nft_copy(fnt$a_char_directory);
+ chardir_offset = longword;
+ IF chardir_offset <= 0 | chardir_offset > nftlen
+ THEN GOTO bad_format;
+
+/* Figure out what the lowest character in the NFT file is */
+
+ %REPLACE fnt$l_first_character BY 164;
+ DCL nft_first_ch FIXED;
+
+ nft_first_ch = rank(nfta(fnt$l_first_character));
+
+/* Now, determine if this is the first or last TXF which is being copied
+into this file. */
+
+ DCL (first_txfc,last_txfc) FIXED;
+ DCL (first_txf,last_txf) BIT;
+
+ first_txfc = -1; last_txfc = -1;
+ DO i = 0 TO 255;
+ IF txfa(txfno) -> chu(i) ^= byte(0) THEN DO;
+ IF first_txfc = -1 THEN first_txfc = rank(txfa(txfno) -> chu(i));
+ last_txfc = rank(txfa(txfno) -> chu(i));
+ END;
+ END;
+
+ IF first_txfc = -1 | last_txfc = -1 THEN DO;
+ PUT SKIP EDIT ('LN03Topp internal error - empty TXF passed to ' ||
+ 'ADD_TXF_TO_LNF') (A);
+ GOTO error_return;
+ END;
+
+ first_txf = (first_txfc = leftfirst+nblanks);
+ last_txf = (last_txfc = lastch(lnfno));
+
+/* If this is the first TXF, then we have to initialize the font buffer */
+
+ IF first_txf THEN DO;
+ using_r = false;
+ CALL initialize_lnf;
+ END;
+
+/* The initialization routine computes where the rasters begin in the
+output buffer, puts in the blanks, etc. */
+
+ DCL (lnf_chardir,ras_beg) FIXED STATIC;
+ DCL ras_len_added FIXED;
+
+ ras_len_added = 0;
+
+/* Now go through all the characters in the TeX font and copy them into the
+right position */
+
+ DO i = 0 TO 255;
+ IF txfa(txfno) -> chu(i) ^= byte(0)
+ THEN DO;
+ j = rank(txfa(txfno) -> chu(i));
+ IF ^using_r & j >= rightfirst THEN DO;
+ CALL write_lnf;
+ PUT SKIP EDIT (',') (A) FILE(outf);
+ using_r = true;
+ CALL initialize_lnf;
+ END;
+ CALL copy_char(i,j);
+ END;
+ END;
+
+/* If this is the last TXF for this LNF, write out the LNF. */
+
+ IF last_txf THEN CALL write_lnf;
+
+
+/* What we have to return is the incremental amount of space that the TXF
+takes up in the output font */
+
+ PUT EDIT (' with ' || cvis(ras_len_added+4*(txfa(txfno) -> nchs+nblanks))
+ || ' bytes of rasters ') (A);
+
+ RETURN(ras_len_added+4*(txfa(txfno) -> nchs+nblanks));
+
+error_return:
+ RETURN(-1);
+
+rasters_too_big:
+ RETURN(-2);
+
+bad_format:
+ RETURN(-3);
+
+ read_into_nfta: PROCEDURE;
+
+ DCL nftf FILE;
+ DCL nftline CHAR(512) VARYING;
+
+ ON ENDFILE (nftf) GOTO end_of_nftfile;
+ ON UNDEFINEDFILE (nftf) BEGIN;
+ PUT SKIP LIST('No such file:' || nftfname);
+ GOTO error_return;
+ END;
+
+ nftlen = 0;
+ OPEN FILE (nftf) TITLE (nftfname)
+ ENVIRONMENT(Default_file_name('TEX$LN03DIR:AMR10.NFT'))
+ RECORD;
+
+ DO WHILE (true);
+
+ READ FILE(nftf) INTO (nftline);
+ IF nftlen+length(nftline) > obuflen THEN DO;
+ PUT SKIP EDIT ('NFT file ' || nftfname || ' too large')
+ (A);
+ GOTO error_return;
+ END;
+ DO i = 1 TO length(nftline);
+ nfta(nftlen+i-1) = substr(nftline,i,1);
+ END;
+ nftlen = nftlen+length(nftline);
+
+ END;
+
+ end_of_nftfile:
+ CLOSE FILE (nftf);
+
+ END read_into_nfta;
+
+/* Copy the character corresponding to FROM out of NFTA and into the LNFR;
+update the CHW array. */
+
+ copy_char: PROCEDURE (from,to);
+ DCL (from,to,i,j) FIXED;
+ DCL (def_start,rows,columns,quo,len) FIXED;
+
+/* First we need to determine where the character definition for FROM
+begins in the NFTA array */
+
+ CALL nft_copy(chardir_offset+(from-nft_first_ch)*4);
+ def_start = longword;
+
+/* For the moment, if there is "no such character", we just stop.
+Eventually we should substitute some blank character or something. */
+
+ IF def_start < chardir_offset | def_start > nftlen THEN
+ GOTO bad_format;
+
+/* Now we need to check that the rasters are uncompressed. */
+
+ IF nfta(def_start+17) ^= byte(129) THEN DO;
+ PUT SKIP EDIT ('Can''t handle compressed rasters') (A);
+ GOTO bad_format;
+ END;
+
+/* Fill in the CHW array. Gutenberg to pixel conversion is necessary (there
+are 24 gutenbergs in a pixel), with rounding (hence the 12). */
+
+ CALL nft_copy(def_start+4);
+ chw(lnfno,to) = DIVIDE(longword+12,24,31);
+
+/* Because of the LN03's rounding algorithm, it is necessary also to
+change the value being copied to the rounded value */
+
+ longword = 24*chw(lnfno,to);
+ nfta(def_start+4) = substr(chars,1,1);
+ nfta(def_start+5) = substr(chars,2,1);
+ nfta(def_start+6) = substr(chars,3,1);
+ nfta(def_start+7) = substr(chars,4,1);
+
+/* Now we need to compute the size of the rasters, and copy that number of
+bytes into LNF as the case may be. */
+
+ rows = rank(nfta(def_start+20))+256*rank(nfta(def_start+21));
+ columns = rank(nfta(def_start+22))+256*rank(nfta(def_start+23));
+
+/* We have to update PSIZE, LSIZE and MSIZE to fill slots in the format. */
+
+ quo = DIVIDE(rows,8,31);
+ IF rows ^= quo*8 THEN quo = quo+1;
+ i = quo*columns;
+ lsize = lsize+quo*columns;
+ quo = DIVIDE(columns,8,31);
+ IF columns ^= quo*8 THEN quo = quo+1;
+ psize = psize+quo*rows;
+ IF i > quo*rows THEN msize = msize+i; ELSE msize = msize+quo*rows;
+
+/* Now do the actual copy */
+
+ IF MOD(rank(nfta(def_start+16)),2) = 0 THEN len = quo*rows;
+ ELSE len = i;
+ len = len+24;
+ DO i = 0 TO len-1;
+ lnf(ras_beg+ras_len+i) = nfta(def_start+i);
+ END;
+ longword = ras_len+ras_beg;
+ IF to <= leftlast THEN i = to-leftfirst; ELSE i = to-rightfirst;
+ lnf(lnf_chardir+4*i) = substr(chars,1,1);
+ lnf(lnf_chardir+4*i+1) = substr(chars,2,1);
+ lnf(lnf_chardir+4*i+2) = substr(chars,3,1);
+ lnf(lnf_chardir+4*i+3) = substr(chars,4,1);
+ ras_len = ras_len+len;
+ ras_len_added = ras_len_added+len;
+ IF MOD(ras_len,2) ^= 0 THEN DO;
+ ras_len = ras_len+1;
+ ras_len_added = ras_len_added+1;
+ END;
+
+ END copy_char;
+
+/* It would be much more efficient if the following were a macro. Later on
+maybe we'll do the macro expansion "by hand" in the code. */
+
+ nft_copy: PROCEDURE (index);
+ DCL index FIXED;
+
+ substr(chars,1,1) = nfta(index);
+ substr(chars,2,1) = nfta(index+1);
+ substr(chars,3,1) = nfta(index+2);
+ substr(chars,4,1) = nfta(index+3);
+
+ END nft_copy;
+
+/* write_lnf performs the last fixups to the lnf buffer and dumps it,
+sixelized, into the OUTF. */
+
+ write_lnf: PROCEDURE;
+
+ DCL preline CHAR(96);
+ DCL (i,j,k,rem,quo,ma) FIXED;
+
+/* We now have to fix a number of slots: size of file, length of rasters,
+final 'FONT', psize, lsize, msize. */
+
+ longword = ras_beg+ras_len+8;
+ lnf(0) = substr(chars,1,1);
+ lnf(1) = substr(chars,2,1);
+ lnf(2) = substr(chars,3,1);
+ lnf(3) = substr(chars,4,1);
+ lnf(longword-8) = substr(chars,1,1);
+ lnf(longword-7) = substr(chars,2,1);
+ lnf(longword-6) = substr(chars,3,1);
+ lnf(longword-5) = substr(chars,4,1);
+ lnf(longword-4) = 'F';
+ lnf(longword-3) = 'O';
+ lnf(longword-2) = 'N';
+ lnf(longword-1) = 'T';
+
+/* Length of rasters */
+
+ %REPLACE fnt$l_char_definitions_length BY 156;
+ longword = ras_len;
+ lnf(fnt$l_char_definitions_length) = substr(chars,1,1);
+ lnf(fnt$l_char_definitions_length+1) = substr(chars,2,1);
+ lnf(fnt$l_char_definitions_length+2) = substr(chars,3,1);
+ lnf(fnt$l_char_definitions_length+3) = substr(chars,4,1);
+
+ %REPLACE fnt$l_portrait_byte_count BY 228;
+ %REPLACE fnt$l_landscape_byte_count BY 232;
+ %REPLACE fnt$l_mixed_byte_count BY 236;
+
+ longword = psize;
+ lnf(fnt$l_portrait_byte_count) = substr(chars,1,1);
+ lnf(fnt$l_portrait_byte_count+1) = substr(chars,2,1);
+ lnf(fnt$l_portrait_byte_count+2) = substr(chars,3,1);
+ lnf(fnt$l_portrait_byte_count+3) = substr(chars,4,1);
+
+ longword = lsize;
+ lnf(fnt$l_landscape_byte_count) = substr(chars,1,1);
+ lnf(fnt$l_landscape_byte_count+1) = substr(chars,2,1);
+ lnf(fnt$l_landscape_byte_count+2) = substr(chars,3,1);
+ lnf(fnt$l_landscape_byte_count+3) = substr(chars,4,1);
+
+ longword = msize;
+ lnf(fnt$l_mixed_byte_count) = substr(chars,1,1);
+ lnf(fnt$l_mixed_byte_count+1) = substr(chars,2,1);
+ lnf(fnt$l_mixed_byte_count+2) = substr(chars,3,1);
+ lnf(fnt$l_mixed_byte_count+3) = substr(chars,4,1);
+
+/* Now sixelize and write the contents of LNF. */
+
+ CALL sixelize_and_write(ras_beg+ras_len+8);
+
+ sixelize_and_write: PROCEDURE (howmuch);
+ DCL (howmuch,quo,i,j) FIXED;
+
+ quo = DIVIDE(howmuch,96,31);
+ IF howmuch > quo*96 THEN quo = quo+1;
+
+ DO i = 1 TO quo;
+ DO j = 1 TO 96;
+ substr(preline,j,1) = lnf(96*(i-1)+j-1);
+ END;
+ CALL write_preline;
+ END;
+
+ END sixelize_and_write;
+
+ write_preline: PROCEDURE;
+ DCL sixel_line CHAR(128) VARYING;
+ DCL (ix,pllen) FIXED;
+
+ pllen = 96;
+
+ ix = 1;
+ sixel_line = copy('?',128);
+ DO i = 1 TO pllen BY 3;
+ DCL cha CHAR(1);
+ DCL chb BIT(8) BASED (addr(cha));
+ DCL chc FIXED(7) BASED (addr(cha));
+ DCL ocha CHAR(1);
+ DCL ochb BIT(8) BASED(addr(ocha));
+
+ chc = 0;
+ ocha = substr(preline,i,1);
+ substr(chb,1,6) = substr(ochb,3,6);
+ chc = chc + 63;
+ substr(sixel_line,ix,1) = cha;
+ ix = ix+1;
+ chc = 0;
+ substr(chb,5,2) = substr(ochb,1,2);
+ ocha = substr(preline,i+1,1);
+ substr(chb,1,4) = substr(ochb,5,4);
+ chc = chc + 63;
+ substr(sixel_line,ix,1) = cha;
+ ix = ix+1;
+ chc = 0;
+ substr(chb,3,4) = substr(ochb,1,4);
+ ocha = substr(preline,i+2,1);
+ substr(chb,1,2) = substr(ochb,7,2);
+ chc = chc + 63;
+ substr(sixel_line,ix,1) = cha;
+ ix = ix+1;
+ chc = 0;
+ substr(chb,1,6) = substr(ochb,1,6);
+ chc = chc + 63;
+ substr(sixel_line,ix,1) = cha;
+ ix = ix+1;
+ END;
+ PUT SKIP EDIT (sixel_line) (A) FILE(outf);
+
+ END write_preline;
+
+ END write_lnf;
+
+/* Initializes the LNF buffer. */
+
+ initialize_lnf: PROCEDURE;
+
+ DCL (i,j) FIXED;
+
+/* The following array holds good values for bytes 0-479 of an LN03 format
+font. */
+
+ DCL good_opening(0:511) FIXED STATIC INIT(
+ 104,38,0,0,70,79,78,84,
+ 1,0,0,0,31,0,0,0,
+ 20,0,0,0,85,48,48,48,
+ 48,48,48,48,48,50,83,75,
+ 48,48,71,71,48,48,48,49,
+ 85,90,90,90,90,48,50,70,
+ 48,48,48,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 181,7,9,0,11,0,14,0,
+ 0,0,0,0,104,0,0,0,
+ 252,0,0,0,124,0,0,0,
+ 100,1,0,0,120,1,0,0,
+ 224,1,0,0,4,0,0,0,
+ 88,3,0,0,0,0,0,0,
+ 92,3,0,0,48,0,0,0,
+ 92,3,0,0,0,0,0,0,
+ 140,3,0,0,212,34,0,0,
+ 140,3,0,0,33,0,0,0,
+ 126,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,0,0,168,0,0,0,
+ 16,0,0,0,94,0,0,0,
+ 0,0,0,0,94,0,0,0,
+ 0,0,0,0,94,0,0,0,
+ 0,0,0,0,236,25,0,0,
+ 54,25,0,0,14,27,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2,0,0,0,
+ 7,0,0,0,92,3,0,0,
+ 7,0,0,0,99,3,0,0,
+ 16,0,0,0,106,3,0,0,
+ 16,0,0,0,122,3,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 10,0,0,0,244,1,0,0,
+ 0,0,24,0,16,0,0,0,
+ 16,0,0,0,1,0,1,0,
+ 1,0,1,0,0,0,1,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 30,0,0,0,20,0,0,0,
+ 196,255,255,255,20,0,0,0,
+ 106,255,255,255,20,0,0,0,
+ 0,0,1,0,0,0,30,0,
+ 166,255,255,255,0,0,0,0,
+ 40,0,0,0,0,0,0,0,
+ 60,0,0,0,240,0,0,0,
+ 60,0,0,0,100,0,0,0,
+ 240,0,0,0,120,0,0,0,
+ 40,0,0,0,120,0,0,0,
+ 96,255,255,255,136,255,255,255,
+ 186,255,255,255,240,0,0,0,
+ 76,255,255,255,60,0,0,0,
+ 160,0,0,0,120,0,0,0,
+ 20,0,0,0,20,0,0,0,
+ 140,3,0,0,194,3,0,0,
+ 244,3,0,0,160,4,0,0,
+ 254,4,0,0,162,5,0,0,
+ 46,6,0,0,84,6,0,0);
+
+ lnf_chardir = 480; /* Fixed */
+
+ /* Clear out LNF */
+
+ lnf = byte(0);
+ ras_len = 0;
+ psize = 0;
+ lsize = 0;
+ msize = 0;
+
+ /* Copy the good values into LNF */
+
+ DO i = 0 TO 479;
+ lnf(i) = byte(good_opening(i));
+ END;
+
+ /* Set the last character if necessary */
+
+ IF lastch(lnfno) < leftlast & ^using_r
+ THEN lnf(fnt$l_first_character+4) = byte(lastch(lnfno));
+ IF using_r
+ THEN lnf(fnt$l_first_character+4) = byte(lastch(lnfno)-rightfirst+
+ leftfirst);
+
+ /* Set the character counts */
+
+ %REPLACE fnt$l_infile_locators BY 204;
+ %REPLACE fnt$l_character_definitions BY 212;
+ %REPLACE fnt$l_raster_count BY 220;
+
+ i = rank(lnf(fnt$l_first_character+4))-
+ rank(lnf(fnt$l_first_character))+1;
+ ras_beg = lnf_chardir + 4*i + 4 + 48;
+ lnf(fnt$l_infile_locators) = byte(i);
+ lnf(fnt$l_character_definitions) = byte(i);
+ lnf(fnt$l_raster_count) = byte(i);
+
+/* Set the font file id. The font family is dependent on the value of
+LNFNO, U0000nn where nn is LNFNO possibly with a leading zero. The
+character set is 1O if USING_R, otherwise the default from GOOD_OPENING. */
+
+ %REPLACE fnt$t_font_file_id BY 20;
+
+ lnf(fnt$t_font_file_id+5) = byte(DIVIDE(lnfno,10,31)+rank('0'));
+ lnf(fnt$t_font_file_id+6) = byte(MOD(lnfno,10)+rank('0'));
+ IF using_r
+ THEN lnf(fnt$t_font_file_id+20) = 'O';
+
+/* Set various offsets which are dependent on the number of characters in
+the font file. */
+
+ %REPLACE fnt$a_subset_tables BY 128;
+ longword = lnf_chardir+4*i;
+ lnf(fnt$a_subset_tables) = substr(chars,1,1);
+ lnf(fnt$a_subset_tables+1) = substr(chars,2,1);
+ lnf(fnt$a_subset_tables+2) = substr(chars,3,1);
+ lnf(fnt$a_subset_tables+3) = substr(chars,4,1);
+
+ longword = longword+4;
+ lnf(fnt$a_subset_tables+8) = substr(chars,1,1);
+ lnf(fnt$a_subset_tables+9) = substr(chars,2,1);
+ lnf(fnt$a_subset_tables+10) = substr(chars,3,1);
+ lnf(fnt$a_subset_tables+11) = substr(chars,4,1);
+ lnf(fnt$a_subset_tables+16) = substr(chars,1,1);
+ lnf(fnt$a_subset_tables+17) = substr(chars,2,1);
+ lnf(fnt$a_subset_tables+18) = substr(chars,3,1);
+ lnf(fnt$a_subset_tables+19) = substr(chars,4,1);
+
+ longword = longword+48;
+ lnf(fnt$a_subset_tables+24) = substr(chars,1,1);
+ lnf(fnt$a_subset_tables+25) = substr(chars,2,1);
+ lnf(fnt$a_subset_tables+26) = substr(chars,3,1);
+ lnf(fnt$a_subset_tables+27) = substr(chars,4,1);
+
+ %REPLACE fnt$a_char_definitions BY 160;
+ lnf(fnt$a_char_definitions) = substr(chars,1,1);
+ lnf(fnt$a_char_definitions+1) = substr(chars,2,1);
+ lnf(fnt$a_char_definitions+2) = substr(chars,3,1);
+ lnf(fnt$a_char_definitions+3) = substr(chars,4,1);
+
+/* Set up the string pool (48 bytes). */
+
+ DCL pool_beg FIXED;
+
+ pool_beg = ras_beg-48;
+
+ lnf(pool_beg) = '0';
+ IF using_r THEN lnf(pool_beg+1) = '<'; ELSE lnf(pool_beg+1) = 'B';
+ lnf(pool_beg+2) = byte(9);
+ lnf(pool_beg+3) = 'Z';
+ lnf(pool_beg+4) = 'Z';
+ lnf(pool_beg+5) = 'Z';
+ lnf(pool_beg+6) = 'Z';
+
+ DO i = 1 TO 7;
+ lnf(pool_beg+7+i-1) = lnf(fnt$t_font_file_id+i-1);
+ END;
+ DO i = 1 TO 16;
+ lnf(pool_beg+7+7+i-1) = ' ';
+ END;
+ DO i = 1 TO 16;
+ lnf(pool_beg+7+7+16+i-1) = lnf(fnt$t_font_file_id+i-1);
+ END;
+
+/* Now make string descriptors point into the string pool. */
+ %REPLACE fnt$a_char_set BY 260;
+ %REPLACE fnt$a_family_id BY 268;
+ %REPLACE fnt$a_family_name BY 276;
+ %REPLACE fnt$a_font_id BY 284;
+
+ longword = pool_beg;
+ lnf(fnt$a_char_set) = substr(chars,1,1);
+ lnf(fnt$a_char_set+1) = substr(chars,2,1);
+ lnf(fnt$a_char_set+2) = substr(chars,3,1);
+ lnf(fnt$a_char_set+3) = substr(chars,4,1);
+ longword = pool_beg+7;
+ lnf(fnt$a_family_id) = substr(chars,1,1);
+ lnf(fnt$a_family_id+1) = substr(chars,2,1);
+ lnf(fnt$a_family_id+2) = substr(chars,3,1);
+ lnf(fnt$a_family_id+3) = substr(chars,4,1);
+ longword = pool_beg+7+7;
+ lnf(fnt$a_family_name) = substr(chars,1,1);
+ lnf(fnt$a_family_name+1) = substr(chars,2,1);
+ lnf(fnt$a_family_name+2) = substr(chars,3,1);
+ lnf(fnt$a_family_name+3) = substr(chars,4,1);
+ longword = pool_beg+7+7+16;
+ lnf(fnt$a_font_id) = substr(chars,1,1);
+ lnf(fnt$a_font_id+1) = substr(chars,2,1);
+ lnf(fnt$a_font_id+2) = substr(chars,3,1);
+ lnf(fnt$a_font_id+3) = substr(chars,4,1);
+
+/* Mercifully, all remaining font file slots that we can fill now are
+already good as received from GOOD_OPENING. */
+
+ END initialize_lnf;
+
+END add_txf_to_lnf;