/* qmsfonts.c: font module for dvi printing on qms printers * Copyright 1985 Massachusetts Institute of Technology * Some portions were written by Scott Simpson, TRW */ #include #include #include #include "util.h" #include "fonts.h" #include "findfile.h" #define MAXDVIFONTS 256 /* Maximum number of DVI fonts allowed */ #define MAXQMSFONTS 256 /* Maximum number of QMS fonts allowed */ #define QMSMAXBLOCKS 386 /* Stated on qms status page */ #define MAXCHARS 128 /* Max chars per QMS font */ #define MAXFNTDIR 16 /* Max number of dirs to look for fonts */ #define MAXFNTNAMLEN 32 /* Maximum number of chars in a font name */ #define USEROTPXL 1 /* Write and read rotated pxl files */ #define DELETEFONT "^DF%05.5d%c^G" #define DEFINEFONT "^(^DF%05.5d%c%c%4.4s%03.3dT" #define ADDTOFONT "^(^DFI%05.5d%c%c%4.4s%03.3dT" #define DEFINECHAR ",%c%c%03.3d%03.3d%03.3d%c%03.3d%c%03.3d" #define ENDDEF "^G^)" #define SELECTFONT "^IS%05.5d" unsigned char *malloc(); char *strcat(); char *strcpy(); char *strncpy(); char *strncmp(); char *strcmp(); long numerator,denominator,half_denominator,jobmag; char job_orientation,printer_orientation; int freeblocks = QMSMAXBLOCKS; /* free space on printer */ char *hex = "0123456789ABCDEF"; FILE *qms; /* output device */ char *filter_name; /* the name of the calling prog */ char *fntdirvec[MAXFNTDIR]; int fntdirveclen; #define QC_LOADED 0x0001 /* Char has been loaded in the printer */ #define QC_NOGLYPH 0x0002 /* Glyph is still in pxl file */ #define QC_ROTATED 0x0004 /* Glyph has been rotated */ #define QC_NEEDED 0x0008 /* This char is needed on this page */ struct qmschar { short qc_info,qc_qmswidth; short qc_height,qc_width; short qc_xoffset,qc_yoffset; long qc_texwidth,qc_pxlwidth; union {long l; unsigned char *p} qc_glyph; }; #define QF_LOADED 0x0001 /* Font has been loaded into printer */ #define QF_RELOAD 0x0002 /* We have to load more needed chars */ #define QF_FILE 0x0004 /* Font hasn't been loaded from the file */ struct qmsfont { short qf_info; short qf_qmsnumber; short qf_maxheight; long qf_mag; long qf_words_used; long qf_words_needed; long qf_timestamp; char qf_qmsname[4]; long qf_pxl_checksum; long qf_pxl_mag_val; long qf_pxl_design_size; long qf_s; char qf_name[MAXFNTNAMLEN]; char qf_filename[MAXNAMLEN]; struct qmschar qf_char[MAXCHARS]; } *qmsfonts[MAXQMSFONTS]; int nqmsfonts; struct qmsfont *curfnt; /* ptr to current and last font */ long timestamp; /* Timestamp for stamping qms fonts */ /* The dvifont struct provides a (possible) many-to-one mapping * because several DVI fonts can map to the same QMS font during font * substitution. */ struct dvifont { unsigned long df_num; /* DVI font number */ struct qmsfont *df_qmsfont; /* 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= MAXQMSFONTS) croak("too many qms fonts"); if (!(qmsfonts[nqmsfonts++] = fnt = (struct qmsfont *) malloc(sizeof(struct qmsfont)))) croak("malloc %d",sizeof(struct qmsfont)); fnt->qf_mag = nmag; fnt->qf_timestamp = 0; fnt->qf_words_needed = 400; fnt->qf_words_used = 0; fnt->qf_qmsnumber = nqmsfonts + 1000; sprintf(fnt->qf_qmsname,"%04.4d",fnt->qf_qmsnumber); fnt->qf_maxheight = 0; fnt->qf_s = s; strcpy(fnt->qf_name,nname); strcpy(fnt->qf_filename,fname); fnt->qf_info = QF_FILE; return(fnt); } getfontfromfile(fnt) struct qmsfont *fnt; { char fname[MAXNAMLEN]; FILE *f; int i; sprintf(fname,"%sr",fnt->qf_filename); if (USEROTPXL && job_orientation != printer_orientation) { if (!access(fname,4)) readpxlfile(fnt,fname,1); else { readpxlfile(fnt,fnt->qf_filename,0); if (f = fopen(fname,"w")) { fclose(f); for (i = 0; i < MAXCHARS; i++) rotate(&(fnt->qf_char[i]),fnt); writepxlfile(fnt,fname); } } } else readpxlfile(fnt,fnt->qf_filename,0); fnt->qf_info &= ~QF_FILE; } writepxlfile(fnt,fname) struct qmsfont *fnt; char *fname; { FILE *f; long glyphaddrs[MAXCHARS],pxl_dir_ptr; if (!(f = fopen(fname,"w"))) croak("can't open %s to write",fname); put4(1001,f); writeglyphs(fnt,f,glyphaddrs); pxl_dir_ptr = ftell(f) / 4; writepxldir(fnt,f,glyphaddrs); put4(fnt->qf_pxl_checksum,f); put4(fnt->qf_pxl_mag_val,f); put4(fnt->qf_pxl_design_size,f); put4(pxl_dir_ptr,f); put4(1001,f); fclose(f); } writepxldir(fnt,f,glyphaddrs) struct qmsfont *fnt; FILE *f; long glyphaddrs[]; { int i; struct qmschar *c; for (i = 0; i < MAXCHARS; i++) { c = &(fnt->qf_char[i]); put2(c->qc_width,f); put2(c->qc_height,f); put2(c->qc_xoffset,f); put2(c->qc_yoffset,f); put4(glyphaddrs[i],f); put4(c->qc_pxlwidth,f); } } writeglyphs(fnt,f,glyphaddrs) struct qmsfont *fnt; FILE *f; long glyphaddrs[]; { register int j; int i,fbw,bw,row; struct qmschar *c; register unsigned char *p; for (i = 0; i < MAXCHARS; i++) { c = &(fnt->qf_char[i]); if (c->qc_glyph.p) { glyphaddrs[i] = ftell(f) / 4; /* Write out this glyph to the PXL file */ p = c->qc_glyph.p; bw = (c->qc_width + 7) / 8; fbw = ((c->qc_width + 31) / 32) * 4; for (row = 0; row < c->qc_height; row++) { for (j = 0; j < bw; j++) putc(*p++,f); for (j = bw; j < fbw; j++) putc(0,f); } } else glyphaddrs[i] = 0; } } int char_words_needed(c) struct qmschar *c; { if (c->qc_info & QC_ROTATED) return(((c->qc_width + 15) / 16) * c->qc_height); else return(((c->qc_height + 15) / 16) * c->qc_width); } int font_blocks_used(fnt) struct qmsfont *fnt; { return((fnt->qf_words_used + 511) / 512); } int font_blocks_needed(fnt) struct qmsfont *fnt; { return((fnt->qf_words_needed + fnt->qf_words_used + 511) / 512 - font_blocks_used(fnt)); } /* 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_qmsfont = find_qmsfont(area,name,texmag,s); } struct qmsfont * 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_qmsfont->qf_timestamp = timestamp++; return(dvifonts[i].df_qmsfont); } croak("no dvi font %d defined",fontnum); } /* Set a font as the current font. */ f_use_font(fontnum,font_space) unsigned long fontnum; long *font_space; { curfnt = find_dvi_font(fontnum); if (curfnt->qf_info & QF_FILE) getfontfromfile(curfnt); /* Make sure that it's loaded in the printer */ if (!(curfnt->qf_info & QF_LOADED) || (curfnt->qf_info & QF_RELOAD)) download_font(curfnt); /* Start using it */ fprintf(qms,SELECTFONT,curfnt->qf_qmsnumber); *font_space = curfnt->qf_s / 6; } /* Hints as to what fonts are to be used. * This is called once per page to give fonts module hints as what fonts * and characters in those fonts are going to be used on this page. */ f_newpage(fontvec,charvec,veclen) unsigned long fontvec[]; unsigned long charvec[][4]; int veclen; { struct qmschar *c; register int i,j; struct qmsfont *f; for (i = 0; i < veclen; i++) { f = find_dvi_font(fontvec[i]); if (f->qf_info & QF_FILE) getfontfromfile(f); if (!(f->qf_info & QF_LOADED)) { f->qf_words_used = 0; f->qf_words_needed = 400; } for (j = 0; j < MAXCHARS; j++) if (charvec[i][j/32] & (1 << (j % 32))) { c = &(f->qf_char[j]); c->qc_info |= QC_NEEDED; if (!(c->qc_info & QC_LOADED)) { f->qf_info |= QF_RELOAD; f->qf_words_needed += char_words_needed(c); } } else f->qf_char[j].qc_info &= ~QC_NEEDED; } } clear(p,n) char *p; int n; { while (n-- > 0) *p++ = 0; } rotate(c,fnt) struct qmschar *c; struct qmsfont *fnt; { unsigned char *oldglyph = c->qc_glyph.p; unsigned int width = (unsigned int)c->qc_width; unsigned int height = (unsigned int)c->qc_height; int newbyteswide = (c->qc_height + 7) / 8; int oldbyteswide = (c->qc_width + 7) / 8; unsigned char *newglyph,*putbyte; int column; int row = -1; /* Start row at 0 when incremented */ int mask; int i, j; int times; int extractbyte; if (!(c->qc_info & QC_NOGLYPH)) { if (!(newglyph = malloc(newbyteswide * width))) croak("malloc %d",newbyteswide * width); clear(newglyph, newbyteswide * width); for (i = 0; i < oldbyteswide * height; i++) { extractbyte = *(oldglyph + i); if (i % oldbyteswide == 0) row++; times = (i + 1) % oldbyteswide ? 8 : (width + 7) % 8 + 1; for (j = 0, mask = 0x80; j < times; j++, mask >>= 1) { column = (((i % oldbyteswide) == 0) && (j == 0)) ? 0 : (column + 1); if (mask & extractbyte) { putbyte = newglyph + column * newbyteswide + (int)((height - (row + 1)) / 8); *putbyte |= 1 << 7 - ((height + 7) - row) % 8; } } } free(oldglyph); c->qc_glyph.p = newglyph; } c->qc_info |= QC_ROTATED; i = c->qc_width; c->qc_width = c->qc_height; c->qc_height = i; i = c->qc_xoffset; c->qc_xoffset = c->qc_width - c->qc_yoffset - fnt->qf_maxheight; c->qc_yoffset = i; } /* Load a font into the qms. */ download_font(fnt) struct qmsfont *fnt; { register int i; if (font_blocks_needed(fnt) > freeblocks) qmsfree(font_blocks_needed(fnt)); freeblocks -= font_blocks_needed(fnt); fnt->qf_words_used += fnt->qf_words_needed; fnt->qf_words_needed = 0; if (fnt->qf_info & QF_LOADED) { fprintf(qms, ADDTOFONT, fnt->qf_qmsnumber,job_orientation, 1,fnt->qf_qmsname,fnt->qf_maxheight); } else { fprintf(qms, DEFINEFONT, fnt->qf_qmsnumber,job_orientation, 1,fnt->qf_qmsname,fnt->qf_maxheight); } for (i = 0; i < MAXCHARS; i++) if ((fnt->qf_char[i].qc_info & QC_NEEDED) && !(fnt->qf_char[i].qc_info & QC_LOADED)) load_char(i,fnt); fprintf(qms,ENDDEF); fnt->qf_info |= QF_LOADED; fnt->qf_info &= ~QF_RELOAD; } load_char(ch,fnt) unsigned long ch; struct qmsfont *fnt; { register unsigned char *bp; register int bytes; struct qmschar *c = &(fnt->qf_char[ch]); int row,bw,bo; if (job_orientation != printer_orientation && !(c->qc_info & QC_ROTATED)) rotate(c,fnt); bp = c->qc_glyph.p; fprintf(qms,DEFINECHAR,hex[(ch >> 4) & 017],hex[ch & 017], c->qc_qmswidth,c->qc_height,c->qc_width, (c->qc_yoffset < 0) ? '+' : '-',abs(c->qc_yoffset), (c->qc_xoffset < 0) ? '+' : '-',abs(c->qc_xoffset)); bw = (c->qc_width + 7) / 8; bo = ((c->qc_width + 15) / 16) * 2; for (row = 0; row < c->qc_height; row++) { for (bytes = 0; bytes < bw; bytes++) putc(*bp++,qms); if (bw != bo) putc(0,qms); } c->qc_info |= QC_LOADED; } int readpxlfile(fnt,fname,rotated) struct qmsfont *fnt; char *fname; int rotated; { long pxl_id,pxl_dir_ptr; 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->qf_filename); /* Read the last 5 longs from the pxl file */ (void) fseek(f, (long)(-5*4), 2); fnt->qf_pxl_checksum = sget4(f); fnt->qf_pxl_mag_val = sget4(f); fnt->qf_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->qf_filename); if (pxl_dir_ptr != ftell(f) / 4 - 517) croak("%s pxl dir ptr is %x, should be %x", fnt->qf_filename, pxl_dir_ptr, ftell(f) / 4 - 517); debug("pxl: checksum %d mag %d designsize %d dir %x\n", fnt->qf_pxl_checksum,fnt->qf_pxl_mag_val, fnt->qf_pxl_design_size,pxl_dir_ptr); /* Read the directory */ (void) fseek(f, pxl_dir_ptr * 4, 0); getpxldir(fnt,f,rotated); debug(" %s max_height=%d\n",fnt->qf_filename,fnt->qf_maxheight); /* Read in all the glyphs */ getglyphs(fnt,f); (void) fclose(f); } getpxldir(fnt,f,rotated) struct qmsfont *fnt; FILE *f; int rotated; { int i; struct qmschar *c; double ds = ((double) fnt->qf_s) / ((double) (1 << 20)); fnt->qf_maxheight = 0; for (i = 0; i < MAXCHARS; i++) { c = &(fnt->qf_char[i]); c->qc_width = sget2(f); c->qc_height = sget2(f); c->qc_xoffset = sget2(f); c->qc_yoffset = sget2(f); c->qc_glyph.l = get4(f); c->qc_pxlwidth = sget4(f); c->qc_texwidth = (long) (((double) c->qc_pxlwidth) * ds); c->qc_qmswidth = (c->qc_texwidth * numerator + half_denominator) / denominator; if (rotated) { c->qc_info = QC_NOGLYPH | QC_ROTATED; fnt->qf_maxheight = max(fnt->qf_maxheight,c->qc_width); } else { c->qc_info = QC_NOGLYPH; fnt->qf_maxheight = max(fnt->qf_maxheight,c->qc_height); } } } getglyphs(fnt,f) struct qmsfont *fnt; FILE *f; { register int j,row; register unsigned char *p; int i,fbw,bw; struct qmschar *c; for (i = 0; i < MAXCHARS; i++) { c = &(fnt->qf_char[i]); if (c->qc_glyph.l) { (void) fseek(f, c->qc_glyph.l * 4, 0); fbw = ((c->qc_width + 31) / 32) * 4; bw = (c->qc_width + 7) / 8; if (!(p = malloc(bw * c->qc_height))) croak("malloc %d",bw * c->qc_height); c->qc_glyph.p = p; for (row = 0; row < c->qc_height; row++) { for (j = 0; j < bw; j++) *p++ = getc(f); for (j = bw; j < fbw; j++) (void) getc(f); } c->qc_info &= ~QC_NOGLYPH; } } } /* Make sure that the font info for this character is loaded. * Characters are incrementally loaded into the printer. * This procedure is called for every character so it must be fast. */ f_use_char(ch,texwidth,devwidth) unsigned long ch; long *texwidth,*devwidth; { register struct qmschar *c = &(curfnt->qf_char[ch]); *texwidth = c->qc_texwidth; *devwidth = c->qc_qmswidth; } /* free blocks by deleting font(s) using LRU strategy */ int qmsfree(blocks) int blocks; { int i,oldtime; struct qmsfont *oldfont; while (blocks > freeblocks) { oldfont = NULL; oldtime = timestamp; for (i = 0; i < nqmsfonts; i++) if ((qmsfonts[i]->qf_info & QF_LOADED) && (qmsfonts[i]->qf_timestamp < oldtime)) { oldfont = qmsfonts[i]; oldtime = oldfont->qf_timestamp; } if (!oldfont) croak("qmsfree bug"); fprintf(qms,DELETEFONT,oldfont->qf_qmsnumber,job_orientation); freeblocks += font_blocks_used(oldfont); oldfont->qf_info &= ~QF_LOADED; oldfont->qf_words_needed = 400; oldfont->qf_words_used = 0; for (i = 0; i < MAXCHARS; i++) { oldfont->qf_char[i].qc_info &= ~QC_LOADED; if (oldfont->qf_char[i].qc_info & QC_NEEDED) oldfont->qf_words_needed += char_words_needed(&(oldfont->qf_char[i])); } } }