/* * Auxiliary routines for ivd2dvi. Copyright 1988 by Larry Denenberg. * May be freely distributed as long as this notice is retained. */ #include #include "commands.h" #include "global.h" /* Procedures and global variables defined in io.c */ extern unsigned BufSize; extern unsigned_byte CopyByte(), ReadByte(), FReadByte(); extern long FReadUnsigned(), FReadSigned(), ReadUnsigned(), ReadSigned(); extern long CopyWord(); extern void WriteByte(), CopyNBytes(), SkipNBytes(), FSkipNBytes(); extern void InitIOBuffers(), BufOverflow(), WriteNumber(); /* Global variables defined in ivd2dvi.c */ extern char *ProgramName; extern long XInput, XOutput, WInput, WOutput, DeltaH; extern int State; extern boolean ExactOutput; /* Global variables defined here */ int FilePushLevel; /* current excess of PUSH over POP */ int MaxFilePushLevel; /* maximum FilePushLevel seen so far */ char *TeXFontDirs; /* directories to search for TFM files */ int NTeXFontDirs; /* number of directories in TeXFontDirs */ FILE *tfmfp; /* fp for currently open TFM file */ font *CurFont; /* font from which we're typesetting */ font *FirstFont; /* head of linked list of all fonts */ long *AuxStackPointer; /* first unused spot on the aux stack */ long *AuxBeginStack; /* first usable spot on the aux stack */ long *AuxEndStack; /* first location beyond the aux stack */ /* Procedures defined in this file, in order of definition */ void Initializations(); void PushWX(), PopXW(), PushDeltaH(), PopDeltaH(), AuxPush(); long AuxPop(); void PushWrite(), PopWrite(), MaxPushLevelOutput(); font *FindFont(); void FontDefine(), NewFont(); long WordMaybeRead(); unsigned_byte ByteMaybeRead(); void CopyFontDefinition(), SkipFontDefinition(); long CharWidth(); void ReadTFMFile(); boolean TFMOpen(), TFMTrialOpen(); void TFMHeaderLoad(), TFMWidthsLoad(), FontMemoryOverflow(); void BadDVIAbort(); /* * Global initializations. At this point, the command line has been * processed, so we know /BufSize/ and /ProgramName/. After trivial * initializations, we change /TeXFontDirs/ from a colon-separated list * to a sequence of null-terminated strings by simply changing each * colon to a null and keeping a count in /NTeXFontDirs/. Finally, we * set up the /AuxStack/ along with its pointer and end; giving it * four times the size of a small stack since /PushWX/ puts four things * on it at a time. */ void Initializations() { char *pchar; State = LTYPESETTING; CurFont = FirstFont = NULL; MaxFilePushLevel = FilePushLevel = 0; InitIOBuffers(); TeXFontDirs = getenv("TEXFONTS"); if (TeXFontDirs == NULL) TeXFontDirs = TFMAREADEFAULT; if (*TeXFontDirs == ':') TeXFontDirs++; for (NTeXFontDirs = 0, pchar = TeXFontDirs; *pchar; NTeXFontDirs++) { while (*pchar && (*pchar != ':')) pchar++; if (*pchar == ':') *pchar++ = '\0'; } AuxBeginStack = SEQALLOC(4*(BufSize/SMALLBUFDIVISOR), long); AuxEndStack = AuxBeginStack + 4*(BufSize/SMALLBUFDIVISOR); AuxStackPointer = AuxBeginStack; if (AuxBeginStack == NULL) { fprintf(stderr, "\n%s fatal error: can't allocate buffers\n", ProgramName); exit(3); } } /* * Save the horizontal motion parameters on the aux stack. */ void PushWX() { AuxPush(WInput); AuxPush(WOutput); AuxPush(XInput); AuxPush(XOutput); } /* * Restore the horizontal motion parameters from the aux stack. */ void PopXW() { XOutput = AuxPop(); XInput = AuxPop(); WOutput = AuxPop(); WInput = AuxPop(); } /* * Save /DeltaH/. We use the same stack as /PushWX/; only /EndReflect/ * has to worry about the necessary stack discipline. */ void PushDeltaH() { AuxPush(DeltaH); } /* * Restore /DeltaH/. */ void PopDeltaH() { DeltaH = AuxPop(); } /* * Push /value/ onto the auxiliary stack. */ void AuxPush(value) long value; { *AuxStackPointer++ = value; if (AuxStackPointer >= AuxEndStack) BufOverflow(); } /* * Pop a value from the auxiliary stack. Underflow means that * something is seriously wrong; even bad input shouldn't do it. */ long AuxPop() { if (--AuxStackPointer < AuxBeginStack) { fprintf(stderr, "\n%s internal error: simulation stack underflow\n", ProgramName); exit(4); } return *AuxStackPointer; } /* * Write a PUSH command to the output DVI file. All such commands must * be written by this routine so we can keep an accurate count of how * deep we are. /FilePushLevel/ keeps this count and /MaxFilePushLevel/ * keeps track of the deepest we've ever been. (This routine should be * called WritePush, but then its name would conflict with WritePop.) */ void PushWrite() { WriteByte(PUSH); if (++FilePushLevel > MaxFilePushLevel) MaxFilePushLevel = FilePushLevel; } /* * Write a POP command to the output DVI file. All such commands must * be written by this routine, so we can keep an accurate count of how * deep we are. The error here shouldn't occur no matter what garbage * is in the DVI file since this stuff is checked at an even lower * level; see /NestingValidate/ in io.c. */ void PopWrite() { WriteByte(POP); if (--FilePushLevel < 0) { fprintf(stderr, "\n%s internal error: more pops than pushes!\n", ProgramName); exit(4); } } /* * Write out the maximum stack depth necessary to process this file. * That number is exactly /MaxFilePushLevel/, as computed by the two * routines immediately preceding. However, we start by reading the * old value, and use it instead if it's big enough and the -X flag * was used. Here's the reason: TeX doesn't always put the correct * value in this field! If TeX is writing out a POP that immediately * follows a PUSH, they are both dropped and nothing goes to the DVI * file. But the variable that's worrying about the maximum stack * depth is not informed of this optimization. So if TeX tries to * write "PUSH PUSH PUSH POP POP POP" it thinks that the max stack * depth is 3, but in fact nothing is written. Thus TeX's behavior * is always conservative. See paragraphs 601, 619, and 629 of * Volume B of {\it Computers and Typesetting}. */ void MaxPushLevelOutput() { long inputlevel; inputlevel = ReadUnsigned(2); if (ExactOutput && (inputlevel > MaxFilePushLevel)) WriteNumber(inputlevel, 2); else WriteNumber((long) MaxFilePushLevel, 2); } /* * Find the font associated with /fontnumber/ by searching the list. * If it's not there, return NULL and let the caller worry. */ font * FindFont(fontnumber) long fontnumber; { font *f; f = FirstFont; while ((f != NULL) && (f->number != fontnumber)) f = f->nextfont; return f; } /* * Define a new font as required by a FNT_DEFn command. If the font is * already known, we must have seen this FNT_DEFn before---skip it if * simulating, otherwise copy it over. (A given FNT_DEFn command may be * seen any number of times because we must process them whether we're * simulating or typesetting.) Call /NewFont/ on never-yet-seen fonts. */ void FontDefine(bytes) unsigned bytes; { long fontnumber; fontnumber = ReadUnsigned(bytes); if (FindFont(fontnumber) == NULL) NewFont(fontnumber, bytes); else if (State >= SIMULATING) SkipFontDefinition(); else CopyFontDefinition(fontnumber, bytes); } /* * Process a font for the first time. If we're not simulating, we must * copy the FNT_DEFn command and parameters into the output file as we * read them. Allocate the new font object and link it into the list. * Fill in the /number/, /checksum/, /scalefactor/, /area/, and /name/ * fields from the arguments (skip the design size; we don't need it). * Set /loaded/ to FALSE to indicate that we haven't read the TFM file. * Only when we need the width of a character in the font---when * /CharWidth/ is called---do we load the TFM file and fill in the rest * of the fields. That is, we never read the TFM files of fonts that * aren't used in reflected text. */ void NewFont(fontnumber, bytes) long fontnumber; unsigned bytes; { font *newfont; unsigned areasize, namesize; char *p; if (State < SIMULATING) { WriteByte(FNT_DEF1 + bytes - 1); WriteNumber(fontnumber, bytes); } newfont = OBJALLOC(font); if (newfont == NULL) FontMemoryOverflow(); newfont->nextfont = FirstFont; FirstFont = newfont; newfont->number = fontnumber; newfont->checksum = WordMaybeRead(); newfont->scalefactor = WordMaybeRead(); WordMaybeRead(); areasize = ByteMaybeRead(); namesize = ByteMaybeRead(); newfont->area = (char *) calloc(areasize+1, sizeof(char)); if (newfont->area == NULL) FontMemoryOverflow(); for (p = newfont->area; areasize != 0; areasize--) *p++ = ByteMaybeRead(); *p = '\0'; newfont->name = (char *) calloc(namesize+1, sizeof(char)); if (newfont->name == NULL) FontMemoryOverflow(); for (p = newfont->name; namesize != 0; namesize--) *p++ = ByteMaybeRead(); *p = '\0'; newfont->loaded = FALSE; } /* * Either read or copy a word, depending on whether we're simulating. * This routine and the next are useful because the definition of a * font might be seen for the first time during a simulation or * during normal left-to-right typesetting. Thus /NewFont/ might or * might not want to copy out parameters as they're read. */ long WordMaybeRead() { if (State >= SIMULATING) return ReadSigned(4); else return CopyWord(); } /* * Either read or copy a byte, depending on whether we're simulating. */ unsigned_byte ByteMaybeRead() { if (State >= SIMULATING) return ReadUnsigned(1); else return CopyByte(); } /* * Copy a font definition from the input DVI file to the output file. */ void CopyFontDefinition(fontnumber, bytes) long fontnumber; unsigned bytes; { WriteByte(FNT_DEF1 + bytes - 1); WriteNumber(fontnumber, bytes); CopyNBytes(12L); CopyNBytes((long) (CopyByte() + CopyByte())); } /* * Skip by a font definition. The FNT_DEFn command and the first * parameter have already been skipped. */ void SkipFontDefinition() { SkipNBytes(12L); SkipNBytes((long) (ReadUnsigned(1) + ReadUnsigned(1))); } /* * Find the width of character /c/ in /CurFont/. If the TFM file hasn't * been read, read it. The /widths/ field is NULL in fonts whose TFM * files we couldn't find; we just return 0 as the width of any character * in such a font. Carefully reduce /c/ modulo 256 if necessary. If /c/ * is now between /bc/ and /ec/, it's a character in the font. Get its * value from the /charwidths/ table (whose indices run from 0 to * /ec/-/bc/) and use that number to index the /widths/ table. If /c/ * isn't in the font, issue a warning and return 0. */ long CharWidth(c) long c; { if (CurFont->loaded == FALSE) ReadTFMFile(CurFont); if (CurFont->widths == NULL) return 0L; if (c < 0) c = 255 - ((-1 - c) % 256); else if (c >= 256) c = c % 256; if ((c >= CurFont->bc) && (c <= CurFont->ec) && (CurFont->charwidths[c - CurFont->bc] != 0)) return CurFont->widths[CurFont->charwidths[c - CurFont->bc]]; fprintf(stderr, "\n%s warning: character %d undefined in font %s\n", ProgramName, c, CurFont->name); return 0L; } /* * Read in the width information for a font. This data isn't read until * we actually need it, viz., the first time /CharWidth/ is called with * /f/ in /CurFont/. If we can't find the TFM file, we set /f->widths/ * to NULL as a flag meaning "assume all widths are zero." In any case, * we mark /f/ as "loaded" so we don't do this again. */ void ReadTFMFile(f) font *f; { if (TFMOpen(f)) { TFMHeaderLoad(f); TFMWidthsLoad(f); (void) fclose(tfmfp); } else f->widths = NULL; f->loaded = TRUE; } /* * Open the TFM file for a font, returning TRUE if we did so and FALSE * otherwise. If the name of the font starts with a slash, we only * try to open the exact file. Otherwise we try prepending each of * the possible TFM directories until we get a hit or run out. Recall * that at this point /TeXFontDirs/ is a null-separated list of names. */ boolean TFMOpen(f) font *f; { char *pchar; int ndir; if ((*f->area == '/') || ((!*f->area) && (*f->name == '/'))) { if (TFMTrialOpen("", f->area, f->name)) return TRUE; } else { for (pchar = TeXFontDirs, ndir = NTeXFontDirs; ndir > 0; ndir--) { if (TFMTrialOpen(pchar, f->area, f->name)) return TRUE; while (*pchar++); } } fprintf(stderr, "\n%s warning: Can't find TFM file for font %s; ", ProgramName, f->name); fprintf(stderr, "assuming zero width\n"); return FALSE; } /* * Try to open a font file. The "area" is mostly empty for TeX, but * you never can tell. On success, leave the file descriptor in the * global variable /tfmfp/ and return TRUE, otherwise return FALSE. */ boolean TFMTrialOpen(path,area,name) char *path, *area, *name; { static char buffer[MAXFILENAMESIZE]; (void) sprintf(buffer, "%s/%s/%s.tfm", path, area, name); tfmfp = fopen(buffer, "r"); if (tfmfp == NULL) return FALSE; else return TRUE; } /* * Set up the remaining fields of the font structure. Get and store * the first and last character codes and the number of distinct widths * in the font. Allocate the tables /charwidths/ and /widths/. Check * the checksum, but skip by the rest of the header. Finally, read the * values into the /charwidths/ table. */ void TFMHeaderLoad(f) font *f; { int nchars; long lh; unsigned_byte *pbyte; FSkipNBytes(tfmfp,2L); lh = FReadUnsigned(tfmfp,2); f->bc = FReadUnsigned(tfmfp,2); f->ec = FReadUnsigned(tfmfp,2); nchars = f->ec - f->bc + 1; if (nchars < 0) nchars = 0; if (nchars > 0) { f->charwidths = SEQALLOC(nchars, unsigned_byte); if (f->charwidths == NULL) FontMemoryOverflow(); } f->nw = FReadUnsigned(tfmfp,2); if (f->nw > 0) { f->widths = SEQALLOC(nchars, long); if (f->widths == NULL) FontMemoryOverflow(); } FSkipNBytes(tfmfp,14L); if (f->checksum != FReadSigned(tfmfp,4)) fprintf(stderr, "\n%s warning: checksum mismatch on font %s\n", ProgramName, f->name); FSkipNBytes(tfmfp, 4*lh - 4); for (pbyte = f->charwidths; nchars > 0; nchars--) { *pbyte++ = FReadByte(tfmfp); FSkipNBytes(tfmfp,3L); } } /* * Read the character widths of a font from the TFM file and convert * to DVI units, a calculation that must be done with extreme care. * Further explanation is punted to section 571 of volume B of the * Encyclop\ae dia TeXnica. The results go into the /widths/ table. */ void TFMWidthsLoad(f) font *f; { int nwidths; unsigned_byte a,b,c,d; long *pwidth,z,alpha,beta; z = f->scalefactor; alpha = 16; while (z >= 040000000L) { z = z/2; alpha = alpha+alpha; } beta = 256/alpha; alpha *= z; nwidths = f->nw; for (pwidth = f->widths; nwidths > 0; nwidths--) { a = FReadByte(tfmfp); b = FReadByte(tfmfp); c = FReadByte(tfmfp); d = FReadByte(tfmfp); *pwidth = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta; if (a != 0) { if (a != 255) { fprintf(stderr, "\n%s fatal error: Bad TFM file for font %s\n", ProgramName, f->name); exit(2); } else *pwidth -= alpha; } pwidth++; } } /* * Do you need comments for this one? */ void FontMemoryOverflow() { fprintf(stderr, "\n%s: Insufficient memory for font definitions\n", ProgramName); exit(3); } /* * Abort the run due to junk in the input. */ void BadDVIAbort(message) char *message; { fprintf(stderr, "\n%s: Malformed DVI file (%s)\n", ProgramName, message); exit(2); }