/* kyofonts.c: font module for dvi printing on Kyocera laser printers * Adapted from the qmsfonts module from Massachusetts Institute of Technology */ #include #include "util.h" #include "fonts.h" #include "findfile.h" #include "kyo.h" #include "preload.h" #include #define MAXDVIFONTS 256 /* Max number of fonts allowed in a DVI file*/ #define MAXFNTDIR 16 /* Max number of dirs to look for fonts */ unsigned char *malloc(); char *strcat(); char *strcpy(); char *strncpy(); char *strncmp(); char *strcmp(); long numerator,denominator,half_denominator,jobmag; long freewords = KYOMAXWORDS; /* free space on printer */ FILE *kyo; /* output device */ char *filter_name; /* the name of the calling prog */ char *fntdirvec[MAXFNTDIR]; int fntdirveclen; int nkyofonts; struct kyofont *curfnt; /* ptr to current and last font */ long timestamp; /* Timestamp for stamping kyo fonts */ int nloaded; /* Number of fonts already loaded */ char *loadfile = PRLOAD ; char fontnums[MAXKYOFONTS]; #define PR_USED 0x0001 #define PR_LOADED 0x0002 struct preload { struct preload_info *pr_font; short pr_info; } loaded[MAXKYOFONTS]; /* The dvifont struct provides a (possible) many-to-one mapping * because several DVI fonts can map to the same Kyocera font during font * substitution. */ struct dvifont { unsigned long df_num; /* DVI font number */ struct kyofont *df_kyofont; /* Font which contains */ } dvifonts[MAXDVIFONTS]; int ndvifonts; long max(a,b) long a,b; { return((a>b)? a:b); } long min(a,b) long a,b; { return((a= 0) && (stat(loadfile, &s) == 0) && (s.st_size > 0) && ((s.st_size % sizeof(struct preload_info)) == 0)) { n = s.st_size / sizeof(struct preload_info); debug("%d fonts were preloaded\n", n); for (i=0; ipr_words_used; fontnums[fnt->pr_kyonumber-KF_NUMBASE] = KF_USED; debug("%s\n", fnt->pr_name); } close(loadf); } else fprintf(kyo, "!R! DAF;EXIT;"); debug("Free font memory %ld words\n",freewords); /* Clear the preload file before going on * So when the driver crashes, it will not leave the next jobs * with incorrect preload information */ close(creat(loadfile, 0644)); } /* Called at the end of the driver program. */ f_term() { int i, j, loadf; struct kyofont *fnt; struct kyochar *c; struct preload_info font; struct preload *pfnt; if ((loadf = creat(loadfile, 0644)) < 0) { debug("Cannot create %s\n", loadfile); return; } /* Just copy back the information for fonts not used by this job */ for (i=0; ipr_info & PR_USED) && (pfnt->pr_info & PR_LOADED)) write(loadf, pfnt->pr_font, sizeof(struct preload_info)); } /* Write back the information about the fonts that are used by this job */ for (i=0; ikf_info & KF_LOADED) { font.pr_kyonumber = fnt->kf_kyonumber; strcpy(font.pr_name, fnt->kf_name); font.pr_mag = fnt->kf_mag; font.pr_words_used = fnt->kf_words_used; font.pr_tfm_checksum = fnt->kf_tfm_checksum; for (j=0; j<4; j++) font.pr_charvec[j] = 0; for (j=0; jkf_char[j]); if (c->kc_info & KC_LOADED) font.pr_charvec[j/32] |= (1 << (j % 32)); } write(loadf, &font, sizeof(struct preload_info)); } } debug( "Font memory left: %ld words\n", freewords); } /* This routine provides for an independent numbering scheme * for the fonts loaded into the Kyocera * It returns the first free number * Fonts are numbered starting at KF_NUMBASE */ short next_kyonumber() { int i; for (i=0; ipr_font; /* The name, magnification and tfm-checksum should match */ if ((pnt->pr_info & PR_LOADED) && (strcmp(fnt->pr_name, name) == 0) && (fnt->pr_mag == mag) && (fnt->pr_tfm_checksum == tfmchecksum)) { return(pnt); } } return((struct preload *) 0); } /* Try to locate the font "name" with the specified attributes * in "area" */ struct kyofont * find_kyofont(area,name,texmag,s,tfmchecksum) char *area,*name; long texmag,s,tfmchecksum; { char fname[MAXNAMLEN],nname[128]; int i,nmag; int mag = (texmag * 3 + 1) / 2; struct kyofont *fnt; struct preload *pfnt; struct preload_info *pnt; /* try to find a font file */ if (!findfile(fntdirvec,fntdirveclen,area,name,mag,fname,nname,&nmag)) croak("no font %s.%d",name,texmag); /* make a new kyocera font */ if (nkyofonts >= MAXKYOFONTS) croak("too many kyo fonts"); if (!(kyofonts[nkyofonts++] = fnt = (struct kyofont *) malloc(sizeof(struct kyofont)))) croak("malloc %d",sizeof(struct kyofont)); fnt->kf_mag = nmag; fnt->kf_timestamp = 0; fnt->kf_s = s; fnt->kf_tfm_checksum = tfmchecksum; strcpy(fnt->kf_name,nname); strcpy(fnt->kf_filename,fname); for (i=0; ikf_char[i].kc_info = 0; if (pfnt = preloaded(nname, tfmchecksum,nmag)) { pnt = pfnt->pr_font; fnt->kf_kyonumber = pnt->pr_kyonumber; fnt->kf_words_needed = 0; fnt->kf_words_used = pnt->pr_words_used; for (i=0; ipr_charvec[i/32] & ( 1 << (i % 32))) fnt->kf_char[i].kc_info = KC_LOADED; fnt->kf_info = KF_FILE | KF_LOADED; pfnt->pr_info |= PR_USED; } else { fnt->kf_kyonumber = next_kyonumber(); fnt->kf_words_needed = KF_MIN; fnt->kf_words_used = 0; fnt->kf_info = KF_FILE; } return(fnt); } /* Read the font information from the pxl file * and remember that it is done */ getfontfromfile(fnt) struct kyofont *fnt; { char fname[MAXNAMLEN]; FILE *f; int i; readpxlfile(fnt,fnt->kf_filename); fnt->kf_info &= ~KF_FILE; } /* Calculate the number of words needed by symbol "c" * for its pxl raster */ int char_words_needed(c) struct kyochar *c; { return(((c->kc_width + 15) / 16) * c->kc_height); } /* Define a font. * Can be called more than once for the same font. */ f_define_font(num,options,area,name,texmag,s,tfmchecksum) char *area,*name; unsigned long num,options,texmag,s,tfmchecksum; { int i; /* check to see if font is already defined */ for(i = 0; i < ndvifonts ; i++) if (dvifonts[i].df_num == num) return; /* Does this make too many fonts defined? */ if (ndvifonts >= MAXDVIFONTS) croak("too many dvi fonts"); dvifonts[ndvifonts].df_num = num; dvifonts[ndvifonts++].df_kyofont = find_kyofont(area,name,texmag ,s,tfmchecksum); } /* Try to find DVI font with number "fontnum" * in the dvi-fonttable * Timestamp it to enable LRU font removal */ struct kyofont * find_dvi_font(fontnum) unsigned long fontnum; { register int i; /* scan through the dvi font list. */ for(i = 0; i < ndvifonts ; i++) if (dvifonts[i].df_num == fontnum) { dvifonts[i].df_kyofont->kf_timestamp = timestamp++; return(dvifonts[i].df_kyofont); } croak("no dvi font %d defined",fontnum); } /* Select a font as the current font */ f_use_font(fontnum,font_space) unsigned long fontnum; long *font_space; { curfnt = find_dvi_font(fontnum); /* The font should be loaded already * because loading fonts while printing a page * could cause the Kyocera to emit the sheet * that is printed on at this moment */ if (!(curfnt->kf_info & KF_LOADED)) croak("cannot load font %d while printing page", fontnum); /* Start using it */ fprintf(kyo,"!R! FONT %1d;EXIT;",curfnt->kf_kyonumber); *font_space = curfnt->kf_s / 6; } /* Put symbol "ch" to the printer * "xsize" is the horizontal width of the character in pixels */ f_setc(ch, xsize) unsigned long ch; int xsize; { register struct kyochar *c = &(curfnt->kf_char[ch]); /* When the symbol is to high it must be sent in raster format * and canoot be loaded into the Kyocera * Symbols higher than MAXKYOHEIGHT pixels cannot be loaded */ if (c->kc_info & KC_RASTER) dev_raster(c); else dev_setc(ch, xsize); } /* Hints as to what fonts are to be used. * This is called once per page to give fonts module instrucions as what fonts * and characters in those fonts have to be loaded for this page. * "fontvec" contains a list of fonts used for this page * "veclen" is the number of fonts used on this page * "charvec" contains the per font information about the symbols needed */ f_newpage(fontvec,charvec,veclen) unsigned long fontvec[]; unsigned long charvec[][4]; int veclen; { struct kyochar *c; register int i,j; struct kyofont *f; for (i = 0; i < veclen; i++) { /* Lookup font in the dvi-fonttable * and read the pxlfile if needed */ f = find_dvi_font(fontvec[i]); if (f->kf_info & KF_FILE) getfontfromfile(f); /* If not not loaded initialize space requirements */ if (!(f->kf_info & KF_LOADED)) { f->kf_words_used = 0; f->kf_words_needed = KF_MIN; } /* Determine which symbols should be loaded from this font */ for (j = 0; j < MAXCHARS; j++) if (charvec[i][j/32] & (1 << (j % 32))) { c = &(f->kf_char[j]); c->kc_info |= KC_NEEDED; if (!(c->kc_info & KC_LOADED)) f->kf_words_needed += char_words_needed(c); } else f->kf_char[j].kc_info &= ~KC_NEEDED; /* Load the symbols needed and not yet loaded */ download_font(f); } } /* Load a font into the Kyocera */ download_font(fnt) struct kyofont *fnt; { register int i; /* Make free space if needed */ if (fnt->kf_words_needed > freewords) kyofree(fnt->kf_words_needed); freewords -= fnt->kf_words_needed; fnt->kf_words_used += fnt->kf_words_needed; /* Load needed symbols that are not yet loaded */ for (i = 0; i < MAXCHARS; i++) if ((fnt->kf_char[i].kc_info & KC_NEEDED) && !(fnt->kf_char[i].kc_info & KC_LOADED)) load_char(i,fnt); /* If font is loaded for the first time set its font attributes */ if (!(fnt->kf_info & KF_LOADED)) fprintf(kyo, "!R! FONT %1d; SFA 0,0,P,0;EXIT;",fnt->kf_kyonumber); debug("Loaded %d words for font %s, %d\n", fnt->kf_words_needed, fnt->kf_name, fnt->kf_kyonumber); fnt->kf_words_needed = 0; fnt->kf_info |= KF_LOADED; } /* Load symbol "ch" from font "fnt" */ load_char(ch,fnt) unsigned long ch; struct kyofont *fnt; { register unsigned short *bp; register int w; struct kyochar *c = &(fnt->kf_char[ch]); int row,bw; /* If symbol is too high it cannot be loaded * and should be send as a raster each time it is used */ if (c->kc_height > MAXKYOHEIGHT) { c->kc_info |= KC_RASTER; return; } /* Does this symbol exist */ if (!(c->kc_glyph.l)) croak("Char %#x not in font %s\n",ch, fnt->kf_name); /* Load metrics information about the symbol */ fprintf(kyo, "!R! LDFC %1d,%1d,%1d,%1d,%1d,%1d,%1d,%1d,0;" ,fnt->kf_kyonumber, visual(ch), c->kc_height, c->kc_width ,32*c->kc_yoffset,32*c->kc_xoffset ,32*c->kc_kyowidth, 16*c->kc_kyowidth); /* Load the pixel raster */ bp = c->kc_glyph.p; bw = (c->kc_width + 15) / 16; for (row = 0; row < c->kc_height; row++) { for (w = 0; w < bw; w++) convert(*bp++); } fprintf(kyo, "; EXIT;"); fflush(kyo); c->kc_info |= KC_LOADED; } /* Convert pixel information into the format needed by the Kyocera */ convert(w) unsigned short w; { char c; c = ((w >> 10) & 077) + 64; if (c == 127) c = '/'; putc(c, kyo); c = ((w >> 4) & 077) + 64; if (c == 127) c = '/'; putc(c, kyo); c = (w & 017) + 48; putc(c, kyo); } /* Read the information for font "fnt" from file "fname" */ int readpxlfile(fnt,fname) struct kyofont *fnt; char *fname; { long pxl_id,pxl_dir_ptr; long pxlchecksum; FILE *f; if (!(f = fopen(fname,"r"))) croak("no pxl file %s",fname); if ((pxl_id = sget4(f)) != 1001) croak("%d bad initial pxl ID; %s doesn't look like a pxl file", pxl_id, fnt->kf_filename); debug("OK This is a PXL file\n"); /* Read the last 5 longs from the pxl file */ (void) fseek(f, (long)(-5*4), 2); pxlchecksum = sget4(f); fnt->kf_pxl_mag_val = sget4(f); fnt->kf_pxl_design_size = sget4(f); pxl_dir_ptr = sget4(f); if ((pxl_id = sget4(f)) != 1001) croak("%d bad final pxl ID; %s doesn't look like a pxl file", pxl_id, fnt->kf_filename); if (pxl_dir_ptr != ftell(f) / 4 - 517) croak("%s pxl dir ptr is %x, should be %x", fnt->kf_filename, pxl_dir_ptr, ftell(f) / 4 - 517); debug("pxl: checksum %d mag %d designsize %d dir %x\n", pxlchecksum,fnt->kf_pxl_mag_val, fnt->kf_pxl_design_size,pxl_dir_ptr); if (fnt->kf_tfm_checksum & (pxlchecksum != fnt->kf_tfm_checksum)) { fprintf(stderr, "Checksum mismatch: %s\n",fnt->kf_filename); fprintf(stderr, " pxl file: %ld, tfm_file: %ld\n", pxlchecksum, fnt->kf_tfm_checksum); } /* Read the directory */ (void) fseek(f, pxl_dir_ptr * 4, 0); getpxldir(fnt,f); debug(" %s max_height=%d\n",fnt->kf_filename,fnt->kf_maxheight); /* Read in all the glyphs */ getglyphs(fnt,f); (void) fclose(f); } /* Read the pixel directory for font "fnt" from file "f" */ getpxldir(fnt,f) struct kyofont *fnt; FILE *f; { int i; struct kyochar *c; double ds = ((double) fnt->kf_s) / ((double) (1 << 20)); /* Each symbol has an entry of 16 bytes */ fnt->kf_maxheight = 0; for (i = 0; i < MAXCHARS; i++) { c = &(fnt->kf_char[i]); c->kc_width = sget2(f); c->kc_height = sget2(f); c->kc_xoffset = sget2(f); c->kc_yoffset = sget2(f); c->kc_glyph.l = get4(f); c->kc_pxlwidth = sget4(f); c->kc_texwidth = (long) (((double) c->kc_pxlwidth) * ds); c->kc_kyowidth = (c->kc_texwidth * numerator + half_denominator) / denominator; /* Determine the maximum height of the font */ fnt->kf_maxheight = max(fnt->kf_maxheight,c->kc_height); } } /* Read the raster information for font "fnt" from file "f" * The information per symbol is a multiple of 4 bytes * The information per symbol that will be loaded is a multiple of 2 bytes */ getglyphs(fnt,f) struct kyofont *fnt; FILE *f; { register int j,row; register unsigned short *p; int i,fbw,bw; struct kyochar *c; for (i = 0; i < MAXCHARS; i++) { c = &(fnt->kf_char[i]); if (c->kc_glyph.l) { (void) fseek(f, c->kc_glyph.l * 4, 0); fbw = ((c->kc_width + 31) / 32) * 2; bw = (c->kc_width + 15) / 16; if (!(p = (unsigned short *)malloc(2*bw * c->kc_height))) croak("malloc %d",2*bw * c->kc_height); c->kc_glyph.p = p; for (row = 0; row < c->kc_height; row++) { for (j = 0; j < bw; j++) *p++ = get2(f); /* get rid of empty trailing bytes */ for (j = bw; j < fbw; j++) (void) get2(f); } } } } /* This procedure is called for every character so it must be fast * It returns the TeX width and the device width of symbol "ch" */ f_use_char(ch,texwidth,devwidth) unsigned long ch; long *texwidth,*devwidth; { register struct kyochar *c = &(curfnt->kf_char[ch]); *texwidth = c->kc_texwidth; *devwidth = c->kc_kyowidth; } /* free words by deleting font(s) using LRU strategy * at least "words" words should be free */ int kyofree(words) int words; { int i,oldtime; struct kyofont *oldfont; struct preload *oldpr; while (words > freewords) { oldpr = NULL; /* First try preloaded fonts not yet used by this job */ for (i=0; ipr_info &= ~PR_LOADED; fprintf(kyo,"!R! DELF %1d;EXIT;",oldpr->pr_font->pr_kyonumber); fflush(kyo); debug("Left free %ld\n", freewords); debug("PRELOADED FONT %s, %d deleted, size %ld\n", oldpr->pr_font->pr_name, oldpr->pr_font->pr_kyonumber, oldpr->pr_font->pr_words_used ); freewords += oldpr->pr_font->pr_words_used; } else { /* Try the other fonts now */ oldfont = NULL; oldtime = timestamp; for (i = 0; i < nkyofonts; i++) if ((kyofonts[i]->kf_info & KF_LOADED) && (kyofonts[i]->kf_timestamp < oldtime)) { oldfont = kyofonts[i]; oldtime = oldfont->kf_timestamp; } if (!oldfont) croak("kyofree bug"); oldfont->kf_info &= ~KF_LOADED; oldfont->kf_words_needed = KF_MIN; oldfont->kf_words_used = 0; fprintf(kyo,"!R! DELF %1d;EXIT;",oldfont->kf_kyonumber); fflush(kyo); debug("Left free %ld\n", freewords); debug("FONT %s, %d deleted, size %ld\n", oldfont->kf_name, oldfont->kf_kyonumber, oldfont->kf_words_used); freewords += oldfont->kf_words_used; for (i = 0; i < MAXCHARS; i++) { oldfont->kf_char[i].kc_info &= ~KC_LOADED; if (oldfont->kf_char[i].kc_info & KC_NEEDED) oldfont->kf_words_needed+=char_words_needed(&(oldfont->kf_char[i])); } } } }