/* * DVI previewer for X. * * Eric Cooper, CMU, September 1985. * * Code derived from dvi-imagen.c. * * Modification history: * 1/1986 Modified for X.10 --Bob Scheifler, MIT LCS. * 7/1988 Modified for X.11 --Mark Eichin, MIT * 12/1988 Added 'R' option, toolkit, magnifying glass * --Paul Vojta, UC Berkeley. * 2/1989 Added tpic support --Jeffrey Lee, U of Toronto * 4/1989 Modified for System V --Donald Richardson, Clarkson Univ. * 3/1990 Added VMS support --Scott Allendorf, U of Iowa * 7/1990 Added reflection mode --Michael Pak, Hebrew U of Jerusalem * 1/1992 Added greyscale code --Till Brychcy, Techn. Univ. Muenchen * and Lee Hetherington, MIT * * Compilation options: * SYSV compile for System V * VMS compile for VMS * X10 compile for X10 * NOTOOL compile without toolkit (X11 only) * BUTTONS compile with buttons on the side of the window (needs toolkit) * MSBITFIRST store bitmaps internally with most significant bit first * BMSHORT store bitmaps in shorts instead of bytes * BMLONG store bitmaps in longs instead of bytes * ALTFONT default for -altfont option * A4 use European size paper * TEXXET support reflection dvi codes (right-to-left typesetting) * GREY use grey levels to shrink fonts */ #include "xdvi.h" #include "dvi.h" #include #ifdef sun extern char *sprintf(); #endif #ifndef X_NOT_STDC_ENV #include #else char *realloc(); #endif #if defined(macII) && !defined(__STDC__) /* stdlib.h doesn't define these */ char *realloc(); #endif /* macII */ #define PK_PRE 247 #define PK_ID 89 #define PK_MAGIC (PK_PRE << 8) + PK_ID #define GF_PRE 247 #define GF_ID 131 #define GF_MAGIC (GF_PRE << 8) + GF_ID #define VF_PRE 247 #define VF_ID_BYTE 202 #define VF_MAGIC (VF_PRE << 8) + VF_ID_BYTE #define PXL_MAGIC1 0 #define PXL_MAGIC2 1001 #define dvi_oops(str) longjmp(dvi_env, (int) str); static struct stat fstatbuf; static Boolean font_not_found; /* * DVI preamble and postamble information. */ static char job_id[300]; static long numerator, denominator, magnification; /* * Offset in DVI file of last page, set in read_postamble(). */ static long last_page_offset; /* * free_vf_chain frees the vf_chain structure. */ static void free_vf_chain(tnp) struct tn *tnp; { while (tnp != NULL) { register struct tn *tnp1 = tnp->next; free((char *) tnp); tnp = tnp1; } } /* * Release all shrunken bitmaps for all fonts. */ void reset_fonts() { register struct font *f; register struct glyph *g; for (f = font_head; f != NULL; f = f->next) if ((f->flags & FONT_LOADED) && !(f->flags & FONT_VIRTUAL)) for (g = f->glyph; g <= f->glyph + f->maxchar; ++g) { if (g->bitmap2.bits) { free(g->bitmap2.bits); g->bitmap2.bits = NULL; } #ifdef GREY if (g->pixmap2) { XDestroyImage(g->image2); g->pixmap2 = NULL; } #endif } } /* * realloc_font allocates the font structure to contain (newsize + 1) * characters. */ void realloc_font(fontp, newsize) struct font *fontp; WIDEARG(ubyte, int) newsize; { struct glyph *glyph; glyph = fontp->glyph = (struct glyph *) realloc((char *) fontp->glyph, ((unsigned int) newsize + 1) * sizeof(struct glyph)); if (glyph == NULL) oops("! Cannot reallocate space for glyph array."); if (newsize > fontp->maxchar) bzero((char *) (glyph + fontp->maxchar + 1), (int) (newsize - fontp->maxchar) * sizeof(struct glyph)); maxchar = fontp->maxchar = newsize; } /* * realloc_virtual_font does the same thing for virtual fonts. */ void realloc_virtual_font(fontp, newsize) struct font *fontp; WIDEARG(ubyte, int) newsize; { struct macro *macro; macro = fontp->macro = (struct macro *) realloc((char *) fontp->macro, ((unsigned int) newsize + 1) * sizeof(struct macro)); if (macro == NULL) oops("! Cannot reallocate space for macro array."); if (newsize > fontp->maxchar) bzero((char *) (macro + fontp->maxchar + 1), (int) (newsize - fontp->maxchar) * sizeof(struct macro)); maxchar = fontp->maxchar = newsize; } /* * load_font locates the raster file and reads the index of characters, * plus whatever other preprocessing is done (depending on the format). */ void load_font(fontp) struct font *fontp; { float fsize = fontp->fsize; int dpi = fsize + 0.5; char *font_found; int size_found; int magic; fontp->flags |= FONT_LOADED; fontp->file = font_open(fontp->fontname, &font_found, (WIDEARG(float, double)) fsize, &size_found, fontp->magstepval, &fontp->filename); if (fontp->file == NULL) { Fprintf(stderr, "Can't find font %s.\n", fontp->fontname); font_not_found = True; return; } --n_files_left; if (font_found != NULL) { Fprintf(stderr, "Can't find font %s; using %s instead at %d dpi\n", fontp->fontname, font_found, dpi); free(fontp->fontname); fontp->fontname = font_found; } else if (size_found > (int) (5 * 1.002 * fsize + 0.5) || size_found < (int) (5 * 0.998 * fsize + 0.5)) Fprintf(stderr, "Can't find font %s at %d dpi; using %d dpi instead.\n", fontp->fontname, dpi, (size_found + 2) / 5); fontp->fsize = (float) size_found / 5; fontp->timestamp = ++current_timestamp; fontp->maxchar = maxchar = 255; fontp->set_char_p = set_char; magic = two(fontp->file); #ifdef USE_PK if (magic == PK_MAGIC) read_PK_index(fontp); else #endif #ifdef USE_GF if (magic == GF_MAGIC) read_GF_index(fontp); else #endif if (magic == VF_MAGIC) read_VF_index(fontp); else #ifdef USE_PXL if (magic == PXL_MAGIC1 && two(fontp->file) == PXL_MAGIC2) read_PXL_index(fontp); else #endif oops("Cannot recognize format for font file %s", fontp->filename); if (fontp->flags & FONT_VIRTUAL) { while (maxchar > 0 && fontp->macro[maxchar].pos == NULL) --maxchar; if (maxchar < 255) realloc_virtual_font(fontp, WIDEARG(,(int)) maxchar); } else { while (maxchar > 0 && fontp->glyph[maxchar].addr == 0) --maxchar; if (maxchar < 255) realloc_font(fontp, WIDEARG(,(int)) maxchar); } } /* * MAGSTEPVALUE - If the given magnification is close to a \magstep * or a \magstephalf, then return twice the number of \magsteps. * Otherwise return NOMAGSTP. */ #define NOMAGSTP (-29999) #define NOBUILD 29999 static int magstepvalue(mag) float *mag; { int m = 0; double fmag = *mag; double xmag = pixels_per_inch; float margin = fmag * 0.002; if (fmag < pixels_per_inch) for (;;) { if (xmag - fmag < margin && -(xmag - fmag) < margin) { *mag = xmag; return m; } if (xmag < fmag) break; xmag *= 0.9128709292; --m; } else for (;;) { if (xmag - fmag < margin && -(xmag - fmag) < margin) { *mag = xmag; return m; } if (xmag > fmag) break; xmag *= 1.095445115; ++m; } return NOMAGSTP; } /* * reuse_font recursively sets the flags for font structures being reused. */ static void reuse_font(fontp) struct font *fontp; { struct tn *tnp; if (fontp->flags & FONT_IN_USE) return; fontp->flags |= FONT_IN_USE; if (list_fonts) Printf("(reusing) %s at %d dpi\n", fontp->fontname, (int) (fontp->fsize + 0.5)); if (fontp->flags & FONT_VIRTUAL) for (tnp = fontp->vf_chain; tnp != NULL; tnp = tnp->next) reuse_font(tnp->fontp); } /* * define_font reads the rest of the fntdef command and then reads in * the specified pixel file, adding it to the global linked-list holding * all of the fonts used in the job. */ void define_font(file, cmnd, vfparent, tn_headpp) FILE *file; WIDEARG(ubyte, unsigned int) cmnd; struct font *vfparent; /* vf parent of this font, or NULL */ struct tn **tn_headpp; /* addr of head of list of TeXnumbers */ { register struct tn *tnp; struct font *fontp; float fsize; double scale_dimconv; int scale; int design; int magstepval; int len; char *fontname; int size; tnp = (struct tn *) xmalloc((unsigned) sizeof(struct tn), "TeXnumber structure"); tnp->next = *tn_headpp; *tn_headpp = tnp; tnp->TeXnumber = num(file, (int) cmnd - FNTDEF1 + 1); (void) four(file); /* checksum */ scale = four(file); design = four(file); len = one(file) + one(file); fontname = xmalloc((unsigned) len + 1, "font name"); Fread(fontname, sizeof(char), len, file); fontname[len] = '\0'; if(debug & DBG_PK) Printf("Define font \"%s\" scale=%d design=%d\n", fontname, scale, design); if (vfparent == NULL) { fsize = 0.001 * scale / design * magnification * pixels_per_inch; scale_dimconv = dimconv; } else { /* * The scaled size is given in units of vfparent->scale * 2 ** -20 * SPELL units, so we convert it into SPELL units by multiplying by * vfparent->dimconv. * The design size is given in units of 2 ** -20 pt, so we convert * into SPELL units by multiplying by * (pixels_per_inch * 2**16) / (72.27 * 2**20). */ fsize = (72.27 * (1<<4)) * vfparent->dimconv * scale / design; scale_dimconv = vfparent->dimconv; } magstepval = magstepvalue(&fsize); size = 5 * fsize + 0.5; /* * reuse font if possible */ for (fontp = font_head;; fontp = fontp->next) { if (fontp == NULL) { /* if font doesn't exist yet */ if (list_fonts) Printf("%s at %d dpi\n", fontname, (int) (fsize + 0.5)); fontp = (struct font *) xmalloc((unsigned) sizeof(struct font), "font structure"); fontp->dimconv = scale * scale_dimconv / (1<<20); fontp->fontname = fontname; fontp->fsize = fsize; fontp->magstepval = magstepval; fontp->flags = FONT_IN_USE; if (vfparent == NULL) load_font(fontp); fontp->next = font_head; font_head = fontp; break; } if (strcmp(fontname, fontp->fontname) == 0 && size == (int) (5 * fontp->fsize + 0.5)) { /* if font already in use */ reuse_font(fontp); free(fontname); break; } } tnp->fontp = fontp; } /* * process_preamble reads the information in the preamble and stores * it into global variables for later use. */ static void process_preamble() { ubyte k; if (one(dvi_file) != PRE) dvi_oops("DVI file doesn't start with preamble"); if (one(dvi_file) != 2) dvi_oops("Wrong version of DVI output for this program"); numerator = four(dvi_file); denominator = four(dvi_file); magnification = four(dvi_file); dimconv = (((double) numerator * magnification) / ((double) denominator * 1000.)); dimconv = dimconv * (((long) pixels_per_inch)<<16) / 254000; specialConv = pixels_per_inch * magnification / 1000000.0; k = one(dvi_file); Fread(job_id, sizeof(char), (int) k, dvi_file); job_id[k] = '\0'; } /* * find_postamble locates the beginning of the postamble * and leaves the file ready to start reading at that location. */ #define TMPSIZ 516 /* 4 trailer bytes + 512 junk bytes allowed */ static void find_postamble() { long pos; ubyte temp[TMPSIZ]; ubyte *p; ubyte *p1; ubyte byte; Fseek(dvi_file, (long) 0, 2); pos = ftell(dvi_file) - TMPSIZ; if (pos < 0) pos = 0; Fseek(dvi_file, pos, 0); p = temp + fread((char *) temp, sizeof(char), TMPSIZ, dvi_file); for (;;) { p1 = p; while (p1 > temp && *(--p1) != TRAILER) ; p = p1; while (p > temp && *(--p) == TRAILER) ; if (p <= p1 - 4) break; /* found 4 TRAILER bytes */ if (p <= temp) dvi_oops("DVI file corrupted"); } pos += p - temp; byte = *p; while (byte == TRAILER) { Fseek(dvi_file, --pos, 0); byte = one(dvi_file); } if (byte != 2) dvi_oops("Wrong version of DVI output for this program"); Fseek(dvi_file, pos - 4, 0); Fseek(dvi_file, sfour(dvi_file), 0); } /* * read_postamble reads the information in the postamble, * storing it into global variables. * It also takes care of reading in all of the pixel files for the fonts * used in the job. */ static void read_postamble() { ubyte cmnd; struct font *fontp; struct font **fontpp; if (one(dvi_file) != POST) dvi_oops("Postamble doesn't begin with POST"); last_page_offset = four(dvi_file); if (numerator != four(dvi_file) || denominator != four(dvi_file) || magnification != four(dvi_file)) dvi_oops("Postamble doesn't match preamble"); /* read largest box height and width */ unshrunk_page_h = (spell_conv(sfour(dvi_file)) >> 16) + offset_y; if (unshrunk_page_h < unshrunk_paper_h) unshrunk_page_h = unshrunk_paper_h; unshrunk_page_w = (spell_conv(sfour(dvi_file)) >> 16) + offset_x; if (unshrunk_page_w < unshrunk_paper_w) unshrunk_page_w = unshrunk_paper_w; (void) two(dvi_file); /* max stack size */ total_pages = two(dvi_file); font_not_found = False; while ((cmnd = one(dvi_file)) >= FNTDEF1 && cmnd <= FNTDEF4) define_font(dvi_file, cmnd, (struct font *) NULL, &tn_head); if (cmnd != POSTPOST) dvi_oops("Non-fntdef command found in postamble"); if (font_not_found) dvi_oops("Not all pixel files were found"); /* * free up fonts no longer in use */ fontpp = &font_head; while ((fontp = *fontpp) != NULL) if (fontp->flags & FONT_IN_USE) fontpp = &fontp->next; else { if (debug & DBG_PK) Printf("Discarding font \"%s\" at %d dpi\n", fontp->fontname, (int) (fontp->fsize + 0.5)); *fontpp = fontp->next; /* remove from list */ free(fontp->fontname); if (fontp->flags & FONT_LOADED) { if (fontp->file != NULL) { Fclose(fontp->file); ++n_files_left; } free(fontp->filename); if (fontp->flags & FONT_VIRTUAL) { register struct macro *m; for (m = fontp->macro; m <= fontp->macro + fontp->maxchar; ++m) if (m->free_me) free((char *) m->pos); free((char *) fontp->macro); free_vf_chain(fontp->vf_chain); } else { register struct glyph *g; for (g = fontp->glyph; g <= fontp->glyph + fontp->maxchar; ++g) { if (g->bitmap.bits != NULL) free(g->bitmap.bits); if (g->bitmap2.bits != NULL) free(g->bitmap2.bits); #ifdef GREY if (g->pixmap2 != NULL) XDestroyImage(g->image2); #endif } free((char *) fontp->glyph); } free((char *) fontp); } } } static void prepare_pages() { int i; page_offset = (long *) xmalloc((unsigned) total_pages * sizeof(long), "page directory"); i = total_pages; page_offset[--i] = last_page_offset; Fseek(dvi_file, last_page_offset, 0); /* * Follow back pointers through pages in the DVI file, * storing the offsets in the page_offset table. */ while (i > 0) { Fseek(dvi_file, (long) (1+4+(9*4)), 1); Fseek(dvi_file, page_offset[--i] = four(dvi_file), 0); } } void init_page() { page_w = ROUNDUP(unshrunk_page_w, mane.shrinkfactor) + 2; page_h = ROUNDUP(unshrunk_page_h, mane.shrinkfactor) + 2; } /* * init_dvi_file is the main subroutine for reading the startup information * from the dvi file. */ static void init_dvi_file() { (void) fstat(fileno(dvi_file), &fstatbuf); dvi_time = fstatbuf.st_mtime; process_preamble(); find_postamble(); read_postamble(); prepare_pages(); init_page(); if (current_page >= total_pages) current_page = total_pages - 1; hush_spec_now = hush_spec; } /** ** open_dvi_file opens the dvi file and calls init_dvi_file() to ** initialize it. **/ void open_dvi_file() { char *errmsg; if ((dvi_file = fopen(dvi_name, OPEN_MODE)) == NULL) { int n = strlen(dvi_name); char *file = dvi_name; if (strcmp(dvi_name + n - sizeof(".dvi") + 1, ".dvi") == 0) { perror(dvi_name); exit(1); } dvi_name = xmalloc((unsigned) n + sizeof(".dvi"), "dvi file name"); Sprintf(dvi_name, "%s.dvi", file); if ((dvi_file = fopen(dvi_name, OPEN_MODE)) == NULL) { perror(dvi_name); exit(1); } } if (errmsg = (char *) setjmp(dvi_env)) oops(errmsg); init_dvi_file(); } /** ** Check for changes in dvi file. **/ Boolean check_dvi_file() { struct font *fontp; if (dvi_file == NULL || fstat(fileno(dvi_file), &fstatbuf) != 0 || fstatbuf.st_mtime != dvi_time) { if (dvi_file) Fclose(dvi_file); free((char *) page_offset); dvi_file = fopen(dvi_name, OPEN_MODE); if (dvi_file == NULL) dvi_oops("Cannot reopen dvi file."); if (list_fonts) Putchar('\n'); free_vf_chain(tn_head); tn_head = NULL; for (fontp = font_head; fontp != NULL; fontp = fontp->next) fontp->flags &= ~FONT_IN_USE; init_dvi_file(); redraw_page(); return False; } return True; }